Spring中beanDefinition合并操作分析
beanDefinition冻结操作
在分析beanDefinition合并操作前,先简单了解一下beanDefinition的冻结操作;
之前说过BeanFactoryPostProcessor这个后置处理器的执行时机是在AbstractApplicationContext#refresh方法里调用invokeBeanFactoryPostProcessors方法时,Spring容器对注册到容器的BeanDefinition所保存的信息做相应的修改,此时的BeanDefinition是加载完成的,而BeanDefinitionRegistryPostProcessor优先于BeanFactoryPostProcessor执行;
冻结beanDefinition,且设置lazyInit为true
测试如下:
查看代码
public class Person {
private final static Log LOG = LogFactory.getLog(Person. class );
public Person() {
LOG.info( "Person constructor" );
}
}
查看代码
public class FreezeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private final static Log LOG = LogFactory.getLog(FreezeBeanFactoryPostProcessor. class );
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
/**
* 冻结beanDefinition
* 设置Person beanDefinition的lazyInit为true
* Person不处于懒加载
*/
GenericBeanDefinition definition = (GenericBeanDefinition) beanFactory.getBeanDefinition( "person" );
// 是否冻结都不影响用户修改beanDefinition,只是针对merge beanDefinition操作
beanFactory.freezeConfiguration();
// definition.setScope(AbstractBeanFactory.SCOPE_PROTOTYPE);
definition.setLazyInit( true );
}
}
查看代码
/**
* 测试beanDefinition冻结
*/
@Test
public void freezeBeanDefinitionTest() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
context.register(Person. class );
context.register(FreezeBeanFactoryPostProcessor. class );
context.refresh();
}
执行效果如下:
明明设置了lazyInit为true,但此时Person Bean还是被实例化了;
在AbstractApplicationContext#refresh执行完invokeBeanFactoryPostProcessors后,观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions分别为true,null;
在AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions分别为true,null;
不冻结beanDefinition,且设置lazyInit为true
之后再测试一下不冻结beanDefinition,且设置lazyInit为true的效果;
/**
* 不冻结beanDefinition
* 设置Person beanDefinition的lazyInit为true
* 此时Person处于懒加载
*/
GenericBeanDefinition definition = (GenericBeanDefinition) beanFactory.getBeanDefinition( "person" );
// 是否冻结都不影响用户修改beanDefinition,只是针对merge beanDefinition操作
// beanFactory.freezeConfiguration();
definition.setLazyInit( true );
执行效果如下:
此时Person Bean没有实例化;
在AbstractApplicationContext#refresh执行完invokeBeanFactoryPostProcessors后,观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions分别为true,null;
在AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions分别为true,null;
之前是修改beanDefinition的lazyInit属性,下面分析一种特殊情况,修改beanDefinition的scope属性;
冻结beanDefinition,设置beanDefinition的scope为prototype
测试如下:
查看代码
public class FreezeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private final static Log LOG = LogFactory.getLog(FreezeBeanFactoryPostProcessor. class );
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
/**
* 冻结beanDefinition
* 设置Person beanDefinition的scope为prototype
* 此时Person属于prototype
*/
GenericBeanDefinition definition = (GenericBeanDefinition) beanFactory.getBeanDefinition( "person" );
// 是否冻结都不影响用户修改beanDefinition,只是针对merge beanDefinition操作
beanFactory.freezeConfiguration();
definition.setScope(AbstractBeanFactory.SCOPE_PROTOTYPE);
}
}
查看代码
/**
* 测试beanDefinition冻结
*/
@Test
public void freezeBeanDefinitionTest() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
context.register(Person. class );
context.register(FreezeBeanFactoryPostProcessor. class );
context.refresh();
System.out.println(context.getBean(Person. class ));
System.out.println(context.getBean(Person. class ));
}
执行结果如下:
此时Person居然被创建了3次,按照之前对getBean的理解,对于prototype类型的Bean,当调用getBean才会对应的Bean才会被创建,测试方法中只调用了getBean两次,还有一次是哪里来的?
在AbstractApplicationContext#refresh执行完invokeBeanFactoryPostProcessors后,观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions中的scope分别为prototype,singleton;
在AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions中的scope分别为prototype,singleton;
之前推断的冻结BeanDefinition只是针对mergedBeanDefinitions,难道错了?再测试一组不冻结beanDefinition,设置beanDefinition的scope为prototype;
不冻结beanDefinition,设置beanDefinition的scope为prototype
测试如下:
查看代码
public class FreezeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private final static Log LOG = LogFactory.getLog(FreezeBeanFactoryPostProcessor. class );
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
/**
* 不冻结beanDefinition
* 设置Person beanDefinition的scope为prototype
* 此时Person属于prototype
*/
GenericBeanDefinition definition = (GenericBeanDefinition) beanFactory.getBeanDefinition( "person" );
// 是否冻结都不影响用户修改beanDefinition,只是针对merge beanDefinition操作
// beanFactory.freezeConfiguration();
definition.setScope(AbstractBeanFactory.SCOPE_PROTOTYPE);
}
}
执行结果:
在AbstractApplicationContext#refresh执行完invokeBeanFactoryPostProcessors后,观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions中的scope分别为prototype,singleton;
在AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMap和mergedBeanDefinitions,如下:
此时beanDefinitionMap和mergedBeanDefinitions中的scope均为prototype;
BeanFactory#freezeConfiguration是用于冻结BeanDefinition,而是否冻结都不影响用户修改beanDefinition,根据上述现象可以推断出冻结BeanDefinition只是针对mergedBeanDefinitions,也就是对应的merge beanDefinition操作;
beanDefinition合并操作分析
在Spring中,beanDefinition加载后会被放置beanDefinitionMap,当Spring容器进行bean对象创建时,这个过程中会先进行beanDefinition的合并;
对于单例对象流程大致如下:
DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>, boolean, boolean)用于根据beanName获取类型,该方法最终会调用到DefaultListableBeanFactory#doGetBeanNamesForType,而getBeanNamesForType在AbstractApplicationContext#refresh调用在invokeBeanFactoryPostProcessors和registerBeanPostProcessors都有使用;
registerBeanPostProcessors方法会调用getBeanNamesForType方法,如下图:
DefaultListableBeanFactory#doGetBeanNamesForType
该方法会调用到AbstractBeanFactory#getMergedLocalBeanDefinition,getMergedLocalBeanDefinition用于beanDefinition合并,此过程的mdb是通过beanDefinitionMap中所有的key去mergedBeanDefinitions中获取的;
AbstractBeanFactory#getMergedLocalBeanDefinition
该方法为beanDefinition的合并操作,如果此时对于beanDefinition的stale属性为true(未冻结),那么DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>, boolean, boolean)方法在下一次调用时就会执行beanDefinition合并操作的逻辑(getMergedBeanDefinition在这个过程只会执行一次,用于创建一个mbd,并与beanName关联放入到mergedBeanDefinitions),否则返回当前的beanDefinition;
合并的触发条件:
- 从mergedBeanDefinitions中获取不到
- beanDefinition的stale属性为true
在AbstractApplicationContext#refresh执行完invokeBeanFactoryPostProcessors后,容器会有调用clearMetadataCache的操作;
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)
这个过程是会清理mergedBeanDefinitions对象的,这里的清理是为了之后的beanDefinition的合并;
在invokeBeanFactoryPostProcessors该方法执行完,Spring已经执行完了父类的扫描,这个过程可能有些父类修改了beanDefinition,因此下一次获取是需要合并,而不是直接从mergedBeanDefinitions中获取;
如第一次BeanDefinition扫描后会进行beanDefinition合并,此时beanDefinitionMap中的数据和mergedBeanDefinitions的数据是同步的,下一次BeanDefinition扫描后,registerBeanDefinition操作会将当前的beanDefinition放入beanDefinitionMap,此时beanDefinitionMap和mergedBeanDefinitions不同步;
Spring的处理通过beanDefinitionMap中所有的key去mergedBeanDefinitions中获取,获取不到的需要进行重新合并,并且使用stale属性作为下一次是否需要重新合并的标识;
AbstractBeanFactory#clearMetadataCache
此时会根据isBeanEligibleForMetadataCaching判断beanDefinition在后面的过程是否需要重新合并,将没有被冻结的beanDefinition中的stale属性置为true,这个属性在后面用于判断beanDefinition是否需要重新进行merge操作;
DefaultListableBeanFactory#isBeanEligibleForMetadataCaching
该方法用于判断beanDefinition在后面的过程是否需要重新合并,configurationFrozen是冻结的标识,而父类中的isBeanEligibleForMetadataCaching方法是根据alreadyCreated集合中是否存储标识正在创建的Bean判断;
stale属性用于标识是否需要重新merge beanDefinition,stale什么时候为true?
AbstractBeanFactory#clearMetadataCache()
AbstractBeanFactory#clearMergedBeanDefinition(java.lang.String)
在进行bean对象创建前会先进行beanDefinition的合并操作
前面测试案例中冻结beanDefinition,设置beanDefinition的scope为prototype,Person Bean一共创建了三次,其中有两次是显式调用getBean方法,下面分析还有一次Bean创建的原因;
在AbstractApplicationContext#refresh中会调用finishBeanFactoryInitialization方法,finishBeanFactoryInitialization方法最终会调用DefaultListableBeanFactory#preInstantiateSingletons,在该方法会根据beanName调用getMergedLocalBeanDefinition方法获取beanDefinition,此时Person Bean的stale依旧是false,那么此时获取的beanDefinition还是之前的,此时beanDefinition的判断是成立的,自然的就会执行getBean方法了;
DefaultListableBeanFactory#preInstantiateSingletons
AbstractBeanFactory#doGetBean为Bean具体创建逻辑
AbstractBeanFactory#doGetBean
也就是说,最终用于创建Bean实例的beanDefinition是合并操作后的;
doGetBean方法会调用markBeanAsCreated,此过程用于标识正在创建的bean,而markBeanAsCreated方法会调用clearMergedBeanDefinition方法,最终会将该beanName对应的beanDefinition的stale属性置为true,如下:
那么冻结beanDefinition,设置beanDefinition的scope为prototype的Bean会执行,执行完markBeanAsCreated方法后,调用getMergedLocalBeanDefinition方法,它就会把getBeanDefinition(beanName)作为参数传到getMergedBeanDefinition方法的逻辑进行beanDefintiion合并,返回的beanDefinition中的scope属性就是prototype的,之后就是根据beanDefinition中的scope创建实例,如下:
对于子beanDefinition是可以继承父beanDefinition的
例子如下:
将父beanDefinition设置为多例类型;
查看代码
@Test
public void childBeanDefinitionTest1() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
// 作为一个父类,也可以是一个普通类
RootBeanDefinition beanADefinition = new RootBeanDefinition(BeanA. class );
beanADefinition.setScope(AbstractBeanDefinition.SCOPE_PROTOTYPE);
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition( "beanA" );
childBeanDefinition.setBeanClass(BeanB. class );
context.registerBeanDefinition( "beanA" , beanADefinition);
context.registerBeanDefinition( "beanB" , childBeanDefinition);
context.refresh();
}
此时BeanA,BeanB都没有被实例化,BeanA的beanDefinition的scope为prototype类型,说明BeanA的beanBeanDefinition被BeanB的beanDefinition继承了;
从上面的例子可以看出,beanDefinition具有继承性;
例子如下:
将子beanDefinition设置为多例类型;
查看代码
@Test
public void childBeanDefinitionTest1() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
// 作为一个父类,也可以是一个普通类
RootBeanDefinition beanADefinition = new RootBeanDefinition(BeanA. class );
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition( "beanA" );
childBeanDefinition.setBeanClass(BeanB. class );
childBeanDefinition.setScope(AbstractBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition( "beanA" , beanADefinition);
context.registerBeanDefinition( "beanB" , childBeanDefinition);
context.refresh();
}
此时只有一个BeanA进行实例化,BeanA的beanDefinition作为父beanDefinition,默认为单例,BeanB的beanDefinition设置为多例,因此BeanB不能被显示实例化;
getMergedLocalBeanDefinition最终会调用到下面方法;
AbstractBeanFactory#getMergedBeanDefinition(java.lang.String, org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.config.BeanDefinition)
也就是getMergedLocalBeanDefinition方法的调用,在实例化前需要将基本的beanDefinition对象转换成RootBeanDefinition,并进行缓存, 后续在需要进行实例化的时候,直接从缓存中取出beanDefinition,当beanDefinition存在继承,即beanDefinition包含了父类,那么需要先创建父类的beanDefinition,之后才能创建子类的beanDefinition,这里就要了合并的意义;
而对于beanDefinition为parent类型的会执行cloneBeanDefinition方法,clone意思是克隆,常见于原型模式,如下:
而这个this表示原始的beanDefinition,beanDefinitionMap中的beanDefinition;
为何需要进行beanDefinition合并的操作?
对于Spring来说,spring不知道当前bean的beanDefinition是否存在继承,因此父子beanDefinition都需要进行merge操作;