spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)

BeanPostProcessor

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(
                  Object bean, String beanName) throws BeansException;
      Object postProcessAfterInitialization(
                  Object bean, String beanName) throws BeansException;

BeanPostProcessor 接口定义bean初始化时的回调,我们可以实现自己的逻辑依赖解析等。我们可以自己实现该接口从而在spring的容器完成初始化配置也可以实现类似plugin的方式。我们可以定义多个类实现该接口,然后定义一下执行的优先级 spring文档中叫order,这样就会按我们设定的order执行你定义的多个类。
ApplicationContext 可以自动检测到任何实现了BeanPostProcessor接口的bean和注册这些bean作为post-processors。在bean创建的过程中调用。
spring-beans module中有FactoryBeanTests这个类,找到第七个测试方法testFactoryBeansWithIntermediateFactoryBeanAutowiringFailure

@Test
    public void testCircularReferenceWithPostProcessor() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        new XmlBeanDefinitionReader(factory).loadBeanDefinitions(CIRCULAR_CONTEXT);

        CountingPostProcessor counter = new CountingPostProcessor();
        factory.addBeanPostProcessor(counter);

        BeanImpl1 impl1 = factory.getBean(BeanImpl1.class);
        assertNotNull(impl1);
        assertNotNull(impl1.getImpl2());
        assertNotNull(impl1.getImpl2());
        assertSame(impl1, impl1.getImpl2().getImpl1());
        assertEquals(1, counter.getCount("bean1"));
        assertEquals(1, counter.getCount("bean2"));
    }

该方法读取的是

spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)_第1张图片
FactoryBeanTests-circular.xml

CountingPostProcessor实现了BeanPostProcessor接口,我们在其中的两个方法中加上日志

public static class CountingPostProcessor implements BeanPostProcessor {

        private final Map count = new HashMap();

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            System.out.println("源码阅读debug postProcessBeforeInitialization:"+bean);
            return bean;
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            System.out.println("源码阅读debug postProcessAfterInitialization:"+bean);
            if (bean instanceof FactoryBean) {
                return bean;
            }
            AtomicInteger c = count.get(beanName);
            if (c == null) {
                c = new AtomicInteger(0);
                count.put(beanName, c);
            }
            c.incrementAndGet();
            return bean;
        }

        public int getCount(String beanName) {
            AtomicInteger c = count.get(beanName);
            if (c != null) {
                return c.intValue();
            }
            else {
                return 0;
            }
        }
    }

我们找到FactoryBeanTests-circular.xml查看其内容




   
      
      
   

   
      
   

   
      
      
   

   
      
   


其中的两个bean比较简单

public static class BeanImpl1 {

        private BeanImpl2 impl2;

        public BeanImpl2 getImpl2() {
            return impl2;
        }

        public void setImpl2(BeanImpl2 impl2) {
            this.impl2 = impl2;
        }
    }


    public static class BeanImpl2 {

        private BeanImpl1 impl1;

        public BeanImpl1 getImpl1() {
            return impl1;
        }

        public void setImpl1(BeanImpl1 impl1) {
            this.impl1 = impl1;
        }
    }

但是注意这两个bean存在循环依赖关系 这个待会再说,先看CountingPostProcessor implements BeanPostProcessor 的两个方法的执行情况,跑一下junit测试 debug

spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)_第2张图片
debug-loadbean

在load的时候会解析xml并实例化bean 实例化得过程会调用到这两个方法

spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)_第3张图片
debug-postProcessAfterInitialization

跑完看一下consle打印的日志
对了先说一下怎么开启spring源码的日志
其实很简单


spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)_第4张图片
log4j.png
# Set root logger level to Debug and its only appender to A1
log4j.rootLogger=ALL,A1
log4j.category.org.springframework=DEBUG

# A1 is set to be ConsoleAppender
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] - %m%n




其中log4j配置下一篇再补充一下
ALL是最小的 就是所有有关的日志都会打印,设置成ALL即可

spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)_第5张图片
log4j-all.png

还有一步要做就是加入编译环境
项目右键Build path

spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式)_第6张图片
config-buildpath.png

这样就可以打日志了
看一下刚刚debug输出的日志

2016-08-30 15:16:16 DEBUG [main] - Adding [systemProperties] PropertySource with lowest search precedence
2016-08-30 15:16:16 DEBUG [main] - Adding [systemEnvironment] PropertySource with lowest search precedence
2016-08-30 15:16:16 DEBUG [main] - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
2016-08-30 15:16:16 INFO [main] - Loading XML bean definitions from class path resource [org/springframework/beans/factory/FactoryBeanTests-circular.xml]
2016-08-30 15:16:16 DEBUG [main] - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
2016-08-30 15:16:16 DEBUG [main] - Loading schema mappings from [META-INF/spring.schemas]
2016-08-30 15:16:16 DEBUG [main] - Loaded schema mappings: {http://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd, http://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd, http://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd, http://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util-4.1.xsd, http://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd, http://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd, http://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd, http://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsd, http://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd, http://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd, http://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsd, http://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd, http://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd, http://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd, http://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd, http://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd, http://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd, http://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd, http://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util-4.0.xsd, http://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsd, http://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd, http://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd, http://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd, http://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd, http://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd, http://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd, http://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd}
2016-08-30 15:16:16 DEBUG [main] - Found XML schema [http://www.springframework.org/schema/beans/spring-beans-3.0.xsd] in classpath: org/springframework/beans/factory/xml/spring-beans-3.0.xsd
2016-08-30 15:16:16 DEBUG [main] - Loading bean definitions
2016-08-30 15:16:27 DEBUG [main] - Creating shared instance of singleton bean 'bean1'
2016-08-30 15:16:27 DEBUG [main] - Creating instance of bean 'bean1'
2016-08-30 15:16:27 DEBUG [main] - Eagerly caching bean 'bean1' to allow for resolving potential circular references
源码阅读debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@659a969b
源码阅读debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@659a969b
2016-08-30 15:18:13 DEBUG [main] - Finished creating instance of bean 'bean1'
2016-08-30 15:18:13 DEBUG [main] - Creating shared instance of singleton bean 'beanImpl1'
2016-08-30 15:18:13 DEBUG [main] - Creating instance of bean 'beanImpl1'
2016-08-30 15:18:16 DEBUG [main] - Eagerly caching bean 'beanImpl1' to allow for resolving potential circular references
2016-08-30 15:18:16 DEBUG [main] - Creating shared instance of singleton bean 'bean2'
2016-08-30 15:18:16 DEBUG [main] - Creating instance of bean 'bean2'
2016-08-30 15:18:16 DEBUG [main] - Eagerly caching bean 'bean2' to allow for resolving potential circular references
源码阅读debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@49b0b76
源码阅读debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@49b0b76
2016-08-30 15:18:19 DEBUG [main] - Finished creating instance of bean 'bean2'
2016-08-30 15:18:19 DEBUG [main] - Creating shared instance of singleton bean 'beanImpl2'
2016-08-30 15:18:19 DEBUG [main] - Creating instance of bean 'beanImpl2'
2016-08-30 15:18:21 DEBUG [main] - Eagerly caching bean 'beanImpl2' to allow for resolving potential circular references
2016-08-30 15:18:21 DEBUG [main] - Returning cached instance of singleton bean 'bean1'
2016-08-30 15:18:21 DEBUG [main] - Returning eagerly cached instance of singleton bean 'beanImpl1' that is not fully initialized yet - a consequence of a circular reference
源码阅读debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl1@769f71a9
源码阅读debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl2@4c9f8c13
源码阅读debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl2@4c9f8c13
2016-08-30 15:18:23 DEBUG [main] - Finished creating instance of bean 'beanImpl2'
源码阅读debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl2@4c9f8c13
源码阅读debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl1@769f71a9
源码阅读debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl1@769f71a9
2016-08-30 15:18:25 DEBUG [main] - Finished creating instance of bean 'beanImpl1'
2016-08-30 15:18:25 DEBUG [main] - Returning cached instance of singleton bean 'beanImpl1'
2016-08-30 15:18:25 DEBUG [main] - Returning cached instance of singleton bean 'bean1'

可以看到日志已经输出,其实仔细读日志基本的初始化过程也可以了解,
接下来看下spring怎么处理两个bean之间的循环依赖的。
其中日志中有几条这样的输出
Eagerly caching bean 'bean1' to allow for resolving potential circular references
2016-08-30 15:18:16 DEBUG [main] - Eagerly caching bean 'beanImpl1' to allow for resolving potential circular references
2016-08-30 15:18:21 DEBUG [main] - Eagerly caching bean 'beanImpl2' to allow for resolving potential circular references
2016-08-30 15:18:21 DEBUG [main] - Returning eagerly cached instance of singleton bean 'beanImpl1' that is not fully initialized yet
最后出现的这条日志其实就是 返回了之前的引用
找到输出这条日志的类AbstractBeanFactorydoGetBean方法 有isSingletonCurrentlyInCreation的判断

if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }

点进去找到源头是DefaultSingletonBeanRegistry
该类实现了spring-core中的接口 这个后续再分析,先看本类声明了需要存储的属性

/** Cache of singleton objects: bean name --> bean instance */
    private final Map singletonObjects = new ConcurrentHashMap(64);

    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map> singletonFactories = new HashMap>(16);

    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map earlySingletonObjects = new HashMap(16);

    /** Set of registered singletons, containing the bean names in registration order */
    private final Set registeredSingletons = new LinkedHashSet(64);

    /** Names of beans that are currently in creation */
    private final Set singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap(16));

    /** Names of beans currently excluded from in creation checks */
    private final Set inCreationCheckExclusions =
            Collections.newSetFromMap(new ConcurrentHashMap(16));

    /** List of suppressed Exceptions, available for associating related causes */
    private Set suppressedExceptions;

    /** Flag that indicates whether we're currently within destroySingletons */
    private boolean singletonsCurrentlyInDestruction = false;

    /** Disposable bean instances: bean name --> disposable instance */
    private final Map disposableBeans = new LinkedHashMap();

    /** Map between containing bean names: bean name --> Set of bean names that the bean contains */
    private final Map> containedBeanMap = new ConcurrentHashMap>(16);

    /** Map between dependent bean names: bean name --> Set of dependent bean names */
    private final Map> dependentBeanMap = new ConcurrentHashMap>(64);

    /** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
    private final Map> dependenciesForBeanMap = new ConcurrentHashMap>(64);

大概浏览下里面的方法
看到有对属性对象的remove和add等操作
其实

singletonFactories,用于存储在spring内部所使用的beanName->对象工厂的引用,一旦最终对象被创建(通过objectFactory.getObject()),此引用信息将删除
earlySingletonObjects,用于存储在创建Bean早期对创建的原始bean的一个引用,注意这里是原始bean,即使用工厂方法或构造方法创建出来的对象,一旦对象最终创建好,此引用信息将删除

最终创建完成bean后这两个对象中都不会存在bean 即size为0
debug发现为了完成最原始得bean注入意思是
bean1 注入的是最原始得bean2 (此时的bean2不包括属性bean1)
bean2注入的也是最原始得bean1(此时的bean1不包括属性bean2)
会放到cached
FactoryBeanRegistrySupport
该类拥有属性

/** Cache of singleton objects created by FactoryBeans: FactoryBean name --> object */
    private final Map factoryBeanObjectCache = new ConcurrentHashMap(16);

两者的循环依赖是基于Setter的依赖注入
基于Setter的依赖注入,是在Bean完成实例化之后,容器调用Bean的setter方法来完成的。
这样的话就解决了循环依赖问题。

你可能感兴趣的:(spring-beans深入源码之BeanPostProcessors(含循环依赖的解决方式))