Loading

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后,观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions分别为truenull

 

AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions分别为truenull

 

不冻结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后,观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions分别为truenull

 

AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions分别为truenull

 

之前是修改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后,观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions中的scope分别为prototypesingleton

 

AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions中的scope分别为prototypesingleton

之前推断的冻结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,如下:

 

此时beanDefinitionMapmergedBeanDefinitions中的scope分别为prototypesingleton; 

 

AbstractApplicationContext#refresh执行完registerBeanPostProcessors后,再观察beanDefinitionMapmergedBeanDefinitions,如下:

 

此时beanDefinitionMapmergedBeanDefinitions中的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,设置beanDefinitionscopeprototype,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对应的beanDefinitionstale属性置为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,这里就要了合并的意义;

 

而对于beanDefinitionparent类型的会执行cloneBeanDefinition方法,clone意思是克隆,常见于原型模式,如下:

而这个this表示原始的beanDefinitionbeanDefinitionMap中的beanDefinition

 

为何需要进行beanDefinition合并的操作?

对于Spring来说,spring不知道当前bean的beanDefinition是否存在继承,因此父子beanDefinition都需要进行merge操作;

 

posted @ 2021-11-03 00:27  街头卖艺的肖邦  阅读(505)  评论(0编辑  收藏  举报