zoukankan      html  css  js  c++  java
  • 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)

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

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

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

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

  • 相关阅读:
    SqlSelect记录搜索
    SqlSelect在查询结果前面加一列序号列
    关于MMC不能打开文件C:\Program Files\Microsoft SQL Server\80\Tools\Binn\SQL Server Enterprise Manager.MSC可能是由于文件不存在,不是一个MMC控制台,或者用后来的MMC版本创建。也可能你没有访问此文件的足够权限
    IsPostBack郁闷
    查询值的数目与目标字段中的数目不同
    SqlServer2000数据库备份
    三种antiTNF制剂生物学剂量增加、非生物制剂和类固醇强化作用的差异:来自临床实践的证据
    三种影像学方法证实依那西普治疗银屑病关节炎有关节修复作用
    北美地区早期和长期类风湿性关节炎患者应用依那西普10年以上的安全性和有效性
    肿瘤坏死因子(TNF)阻断剂治疗幼年型银屑病关节炎: 有效吗
  • 原文地址:https://www.cnblogs.com/coder-zyc/p/15501909.html
Copyright © 2011-2022 走看看