Spring IoC 容器之 BeanFactory
首先,Spring 提供了两种容器类型:BeanFactory 和 ApplicationContext。
- BeanFactory:基础容器类型,提供了完整的 IoC 服务支持。默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中某个对象的时候,猜对该对象进行初始化以及依赖注入操作。适用于资源有限、功能要求不多的场景。
- ApplicationContext:在 BeanFactory 的基础上构建,提供了其他高级功能,如事件发布、国际化支持等。在该类型容器启动后,所管理的对象默认全部初始化并完成依赖注入。适用于资源充足、要求功能完善的场景。
本文主要介绍 BeanFactory。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
BeanFactory 的对象注册
BeanFactory 只是一个接口,我们需要一个实现来管理对象。DefaultListableBeanFactory 间接实现了 BeanFactory,且实现了 BeanDefinitionFactory。该接口担任着 Bean 的注册管理的角色。
![](/i/l/?n=22&i=blog/1499871/202211/1499871-20221107230405377-1864706089.jpg)
每一个受管控的 bean,在容器中都会有一个 BeanDefinition 的实例与之对应,该 BeanDefinition 的实例负责保存对象的所有必要信息,包括对象的 class 类型、是否是抽象类、构造方法参数以及其他属性等。当客户端向 BeanFactory 请求相应对象时,BeanFactory 会通过这些信息给客户端返回一个完备可用的对象实例。RootBeanDefinition 和 ChildBeanDefinition 是两个主要实现类。
实例如下:
public class BeanFactoryTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory beanFactory = bind(beanRegistry);
FXNewsProvider fxNewsProvider1 = beanFactory.getBean(FXNewsProvider.class);
FXNewsProvider fxNewsProvider2 = beanFactory.getBean(FXNewsProvider.class);
System.out.println(fxNewsProvider1 == fxNewsProvider2); // true
}
public static BeanFactory bind(BeanDefinitionRegistry registry) {
AbstractBeanDefinition fxNewsProvider = new RootBeanDefinition(FXNewsProvider.class);
AbstractBeanDefinition dowJonesNewsListener = new RootBeanDefinition(DowJonesNewsListener.class);
AbstractBeanDefinition dowJonesNewsPersister = new RootBeanDefinition(DowJonesNewsPersister.class);
// 将 bean 定义注册到容器中
registry.registerBeanDefinition("fxNewsProvider", fxNewsProvider);
registry.registerBeanDefinition("dowJonesNewsListener", dowJonesNewsListener);
registry.registerBeanDefinition("dowJonesNewsPersister", dowJonesNewsPersister);
// 通过构造方法注入
// ConstructorArgumentValues arguments = new ConstructorArgumentValues();
// arguments.addIndexedArgumentValue(0, dowJonesNewsListener);
// arguments.addIndexedArgumentValue(1, dowJonesNewsPersister);
// fxNewsProvider.setConstructorArgumentValues(arguments);
// 通过 setter 注入
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
mutablePropertyValues.addPropertyValue(new PropertyValue("dowJonesNewsListener", dowJonesNewsListener));
mutablePropertyValues.addPropertyValue(new PropertyValue("dowJonesNewsPersister", dowJonesNewsPersister));
fxNewsProvider.setPropertyValues(mutablePropertyValues);
return (BeanFactory) registry;
}
}
bean 的 scope
Spring 容器最初只有 singleton 和 prototype 两种 scope 类型,后面又引入了三种:request、session 和 global session。
- singleton:在容器中只存在一个实例,所有对该对象的引用都将共享这个实例。该实例从容器启动,并在第一次初始化之后,将一直存活到容器退出。
- prototype:容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例返回。虽然这种类型的对象的实例化和属性设置等工作是由容器负责的,但是主要对象返回后,容器不再拥有该对象的引用,即不再管理该对象。
容器启动的一些细节
Spring 的 IoC 容器,会以某种方式加载 Configuration Metadata(各种配置信息),然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。
Spring 的 IoC 容器可以分成两个阶段,容器启动阶段和 bean 的实例化阶段。
容器的启动阶段
容器启动伊始,首先会通过某种途径加载 Configuration Metadata。在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的 Configuration Metadata 进行解析和分析,并将分析后的信息编组为相应的 BeanDefinition,待注册到相应的 BeanDefinitionRegistry,这样容器启动的主要工作就结束了。
该阶段主要侧重于对象管理信息的收集,一些验证性或者辅助性的工作也可以在这个阶段完成。
bean 的实例化阶段
在第一阶段中,bean 的定义信息都已经注册到 BeanDefinitionRegistry 中。当某个请求通过容器的 getBean 方法明确请求到某个对象或者因依赖关系容器需要隐式调用 getBean 方法时,就会触发 bean 的实例化。
在该阶段,容器首先会检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的 BeanDefinition 所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当对象转配完毕之后,容器会立即返回该对象。
插手容器的启动
BeanFactoryPostProcessor
Spring 提供了 BeanFactoryPostProcessor 的机制,可以允许在容器实例化相应的对象之前,对注册到容器的 BeanDefinition 所保存的信息进行修改。
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
简单看一下 Spring 内置的几个常见的 BeanFactoryPostProcessor。
-
PropertySourcesPlaceholderConfigurer
通常情况下,我们会将例如数据库连接、服务器连接信息等放在一个 properties 文件中,这样,当系统资源变动的话,只需要修改相应的 properties 文件即可。PropertySourcesPlaceholderConfigurer 允许我们在 XML 或者在 Bean 中使用占位符(PlaceHolder,形如
${property}
)。简单来说,当 BeanFactory 在第一阶段加载完成所有的配置信息时,BeanFactory 中保存的对象的属性还是以占位符的形式存在,如
${jdbc.url}
。当 PropertySourcesPlaceholderConfigurer 作为 BeanFactoryPostProcessor 被应用时,它会使用 properties 文件中配置信息替换相应的 BeanDefinition 中占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化 bean 时,bean 定义的属性值就是最终替换完成的了。
bean 的生命周期
BeanFactory 的源码注释中描述了 bean 的生命周期:
实例化 bean:
- BeanNameAware's setBeanName
- BeanClassLoaderAware's setBeanClassLoader
- BeanFactoryAware's setBeanFactory
- EnvironmentAware's setEnvironment
- EmbeddedValueResolverAware's setEmbeddedValueResolver
- ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
- ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
- MessageSourceAware's setMessageSource (only applicable when running in an application context)
- ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
- ServletContextAware's setServletContext (only applicable when running in a web application context)
- postProcessBeforeInitialization methods of BeanPostProcessors
- InitializingBean's afterPropertiesSet
- a custom init-method definition
- postProcessAfterInitialization methods of BeanPostProcessors
销毁 bean:
- postProcessBeforeDestruction methods of DestructionAwareBeanPostProcessors
- DisposableBean's destroy
- a custom destroy-method definition
![](/i/l/?n=22&i=blog/1499871/202211/1499871-20221107230405464-1105669333.jpg)
bean 的实例化和 BeanWrapper
容器内部实例化的时候,采用策略模式来决定采用何种方式进行初始化。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的 bean 的实例或者动态生成其子类。
org.springframework.beans.factory.support.InstantiationStrategy
是实例化策略的抽象接口,其子类 SimpleInstantiationStrategy
实现了简单的对象实例化功能,通过反射来实例化对象实例,但不支持方法注入方式的对象实例化。CglibSubclassingInstantiationStrategy
继承了 SimpleInstantiationStrategy
以反射方式实例化对象的功能,并且通过 CGLIB 的动态字节码生成功能,满足方法注入所需的对象实例化需求。容器默认采用 CglibSubclassingInstantiationStrategy
。
public interface InstantiationStrategy {
/**
* Return an instance of the bean with the given name in this factory.
*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
throws BeansException;
/**
* Return an instance of the bean with the given name in this factory,
* creating it via the given constructor.
*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Constructor<?> ctor, Object... args) throws BeansException;
/**
* Return an instance of the bean with the given name in this factory,
* creating it via the given factory method.
*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, Method factoryMethod, Object... args)
throws BeansException;
}
容器根据 RootBeanDefinition 结合 CglibSubclassingInstantiationStrategy
以及不同的 bean 定义类型,就可以返回实例化完成的对象实例。但是,不是直接返回构造完成的对象实例,而是以 BeanWrapper
对构造完成的对象实例进行包裹。
至此,实例化对象的步骤结束。
BeanWrapper
接口有个实现类 BeanWrapperImpl
,设置 bean 的相应属性值。BeanWrapper
定义继承了 PropertyAccessor
接口,可以以统一的方式对对象属性进行访问;另外,同时继承了 PropertyEditorRegistry
和 TypeConverter
接口,提供属性设值的类型转换等功能。
示例:
public class BeanWrapperTest {
public static void main(String[] args) throws Exception {
final Object fxNewsProvider = Class.forName("com.stm.spring.ioc.bean.FXNewsProvider").newInstance();
final Object dowJonesNewsListener = Class.forName("com.stm.spring.ioc.bean.DowJonesNewsListener").newInstance();
final Object dowJonesNewsPersister = Class.forName("com.stm.spring.ioc.bean.DowJonesNewsPersister").newInstance();
BeanWrapper newsProvider = new BeanWrapperImpl(fxNewsProvider);
newsProvider.setPropertyValue("dowJonesNewsListener", dowJonesNewsListener);
newsProvider.setPropertyValue("dowJonesNewsPersister", dowJonesNewsPersister);
System.out.println(newsProvider.getWrappedInstance() instanceof FXNewsProvider); // true
System.out.println(dowJonesNewsListener == newsProvider.getPropertyValue("dowJonesNewsListener")); // true
System.out.println(dowJonesNewsPersister == newsProvider.getPropertyValue("dowJonesNewsPersister")); // true
}
}
至此,完成了对象属性赋值的步骤。
各种 Aware 接口的处理
当对象实例化完成并且相关属性以及依赖设置完成之后,spring 容器会检查当前对象实例是否实现了一系列以 Aware 命名结尾的接口定义。如果是,则将这些 Aware 接口定义中规定的依赖注入给当前对象实例。
![](/i/l/?n=22&i=blog/1499871/202211/1499871-20221107230405389-2130458118.jpg)
BeanPostProcessor
注意:BeanPostProcessor 用于 bean 的实例化阶段,BeanFactoryPostProcessor 用于容器启动阶段。BeanPostProcessor 会处理容器内所有符合条件的实例化后的对象实例。该接口定义如下:
public interface BeanPostProcessor {
/**
* Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>The default implementation returns the given {@code bean} as-is.
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other {@code BeanPostProcessor} callbacks.
* <p>The default implementation returns the given {@code bean} as-is.
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
常见的使用 BeanPostProcessor 的场景,是处理标记接口实现类,或者为当前对象提供代理实现。例如,ApplicationContext 中的 Aware 接口就是通过 BeanPostProcessor 实现的。参考 ApplicationContextAwareProcessor
源码:
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
/**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
另外,在 CommonAnnotationBeanPostProcessor
类中提供了对 PostConstruct
和 PreDestroy
的支持。
InitializingBean 和 init-method
这两种方式都是在 BeanPostProcessor 的前置处理之后调用。比如在某些情况下,某个业务对象实例化完成后,还不能处于可以使用的状态,这个时候可以让业务对象实现 InitializingBean
,在 afterPropertiesSet
方法中完善对该业务对象的处理。
public interface InitializingBean {
/**
* Invoked by the containing {@code BeanFactory} after it has set all bean properties
* and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
* <p>This method allows the bean instance to perform validation of its overall
* configuration and final initialization when all bean properties have been set.
*/
void afterPropertiesSet() throws Exception;
}
另外,可以指定 init-method,完成类似功能。
DisposableBean 和 destroy-method
两种方式都是为了当 singleton 对象在容器销毁时,提供一个用于对象销毁的回调。
public interface DisposableBean {
/**
* Invoked by the containing {@code BeanFactory} on destruction of a bean.
* @throws Exception in case of shutdown errors. Exceptions will get logged
* but not rethrown to allow other beans to release their resources as well.
*/
void destroy() throws Exception;
}
总结
BeanFactory 作为 spring 提供的基础 IoC 容器,支持对象的注册以及依赖关系绑定。除了 BeanFactory,spring 还提供了 ApplicationContext 容器,其构建于 BeanFactory 之上,提供了更多丰富的功能。
标签:容器,BeanFactory,对象,Spring,bean,实例,IoC,throws From: https://www.cnblogs.com/shitianming/p/16867852.html