Spring 循环依赖以及三级缓存
1. 循环依赖问题
spring创建一个Bean实例分为三步,实例化,依赖注入和初始化。
实例化的方式分为两大类:工厂方法(静态工厂方法和实例工厂) 和 构造器方法(默认和自动装配)。以一个例子来说下,一个类AppleTree,有一个成员变量Apple
public class Apple { Apple() { } } public class AppleTree { private Apple apple; AppleTree(Apple apple) { this.apple = apple; } AppleTree() { } } //工厂类 public class AppleTreeFactory { //静态工厂方法 public static AppleTree getAppleTree() { return new AppleTree(new Apple()); } public AppleTree makeAppleTree() { return new AppleTree(new Apple()); } }
我们实例化AppleTree有4种方式:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- <context:annotation-config/>--> <bean id="apple" class="com.jd.java.easycoding.spring.Apple"/> <!-- 根据构造器自动装配 --> <bean id="appleTree1" class="com.jd.java.easycoding.spring.AppleTree" autowire="constructor"/> <!-- 构造器 --> <bean id="appleTree2" class="com.jd.java.easycoding.spring.AppleTree"> <constructor-arg ref="apple"/> </bean> <!-- 静态工厂方法 --> <bean id="appleTree3" class="com.jd.java.easycoding.spring.AppleTreeFactory" factory-method="getAppleTree"/> <!-- 工厂实例 --> <bean id="appleTree4" class="com.jd.java.easycoding.spring.AppleTree" factory-bean="appleTreeFactory" factory-method="makeAppleTree"/> <bean id="appleTreeFactory" class="com.jd.java.easycoding.spring.AppleTreeFactory"/> </beans>
Bean创建的第二步是进行依赖注入。
依赖注入的方式也有两大类:有参构造器注入和set方法。上面实例化的过程中,如果已经通过有参构造器注入了Apple,则不需要再次注入。如果没有注入,比如通过无参构造器创建实例,那么需要进行属性注入,这时就必须在类中定义对应属性的set方法:我们在AppleTree类中添加一个无参构造器和setApple方法来实验一下
public class AppleTree { private Apple apple; AppleTree(Apple apple) { this.apple = apple; } AppleTree() { } public void setApple(Apple apple) { this.apple = apple; } }
在xml中添加如下配置
<!-- set方法 --> <bean id="appleTree5" class="com.jd.java.easycoding.spring.AppleTree"> <property name="apple" ref="apple"/> </bean>
这种方式,实例化时使用的默认无参构造器,然后在依赖注入时,使用setApple方法将Apple这个Bean注入。如果是公有方法使用JDK内省技术获取set方法,如果是私有方法也没有关系,使用反射暴力获取。
当然还可以使用自动装配特性,有两种,byName和byType,也是通过set方法进行注入的
<!-- 自动装配byName --> <bean id="appleTree6" class="com.jd.java.easycoding.spring.AppleTree" autowire="byName" /> <!-- 自动装配byType --> <bean id="appleTree7" class="com.jd.java.easycoding.spring.AppleTree" autowire="byType" />
下面说下循环依赖的问题,有两个类,ObjectA和ObjectB相互引用。
循环依赖是发生在依赖注入时候的问题,上面已经解释了依赖注入的方式有构造器注入和set注入。
1. 先看下构造器注入的方式
public class ObjectA { ObjectB b; ObjectA(ObjectB b){ this.b = b; } } public class ObjectB { ObjectA a; ObjectB(ObjectA a) { this.a = a; } }
在xml中使用构造器自动装配
<!-- 对象A --> <bean id="objectA" class="com.jd.java.easycoding.spring.ObjectA" autowire="constructor"/> <!-- 对象B --> <bean id="objectB" class="com.jd.java.easycoding.spring.ObjectB" autowire="constructor"/>
在本地进行如下测试,直接一行红字
public class AutoWireTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:bean.xml"}); ObjectA a = (ObjectA) context.getBean("objectA"); } }
2. 尝试通过set方法注入
public class ObjectA { ObjectB b; ObjectA(ObjectB b) { this.b = b; } ObjectA() { } public void setB(ObjectB b) { this.b = b; } } public class ObjectB { ObjectA a; ObjectB(ObjectA a) { this.a = a; } ObjectB() { } public void setObjectA(ObjectA a) { this.a = a; } }
在xml中使用set方式注入
<!-- 对象A --> <bean id="objectA" class="com.jd.java.easycoding.spring.ObjectA" autowire="byName"/> <!-- 对象B --> <bean id="objectB" class="com.jd.java.easycoding.spring.ObjectB" autowire="byName"/>
在本地进行测试,神奇的情况发生了,没有报错。
此处黑人问号?
2.三级缓存
三级缓存是什么?
1. private final Map<String, Object> singletonObjects 一级缓存,缓存单例对象的Bean,是已经完全创建完成的Bean
2. private final Map<String, Object> earlySingletonObjects 二级缓存,提前曝光半成品单例对象或对象的代理
3. private final Map<String, ObjectFactory<?>> singletonFactories 三级缓存,缓存产生单例对象的工厂,通过该工厂能获取到创建一半的Bean
同一时刻,一个Bean只会存放在一个缓存中
2.1 获取Bean的流程
AbstractBeanFactory#doGetBean方法,先通过Bean的名字从缓存中获取单例Bean: Object sharedInstance = getSingleton(beanName); 。这里有一点要注意,如果取出来的是FactoryBean,那么还需要通过FactoryBean的getObject方法来创建对应的Bean(或者从factoryBeanObjectCache缓存中拿)。
有关FactoryBean和BeanFactory:https://www.cnblogs.com/tiancai/p/9604040.html
DefaultSingletonBeanRegistry#getSingleton方法
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从一级缓存获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); //isSingletonCurrentlyInCreation方法: //判断当前单例bean是否正在创建中,还没完全创建完毕。这个标识在开始创建Bean时会设置,下面会见到 //比如一个对象依赖另一个对象,在依赖注入时,需要先创建另一个对象 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //从二级缓存中获取单例bean singletonObject = this.earlySingletonObjects.get(beanName); //allowEarlyReference: 是否允许从singletonFactories中通过getObject拿到对象 if (singletonObject == null && allowEarlyReference) { //从三级缓存中获取单例bean ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //通过单例工厂获取单例bean singletonObject = singletonFactory.getObject(); //从三级缓存移动到了二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); //从三级缓存中删除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
依次从一、二、三级缓存中获取Bean。如果没有获取到Bean,那么获取BeanDefinition,准备创建,从BeanDefinition中得知这个Bean是单例原型还是等等有不同的创建方式。我们看单例的创建模式
AbstractBeanFactory#doGetBean方法中的一小段:
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { // 真正创建单例Bean的方法 AbstractBeanFactory#createBean // 这种写法是Java8的函数式接口,省去了创建对象 return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } // 创建之前,设置一个创建中的标识 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { // 调用匿名内部类获取单例对象,完成Bean的创建 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } // 创建成功之后,删除创建中的标识 afterSingletonCreation(beanName); } // 将产生的单例Bean放入一级缓存中,并从二三级缓存中删除 if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
Bean创建完成,会将Bean放入一级缓存,并从二三级缓存中删除。结合获取Bean的方式,发现在没有循环依赖的情况下,只会从一级缓存中获取Bean,二三级缓存是用不到的。
我们再来看创建Bean的方法。调用链有点长,直接定位到
AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { //单例Bean先从缓存中获取同名Bean instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } // bean初始化第一步:实例化Bean // 构造参数依赖注入,就是发生在这一步 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // 实例化后的Bean对象 final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // 解决循环依赖的关键步骤 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // 如果需要提前暴露单例Bean if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 在发生循环引用的情况下,将刚创建的bean放入三级缓存中(key是beanName,value是FactoryBean) // 通过三级缓存获取Bean,调用的是getEarlyBeanReference方法 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 记录一下,初始化可能返回的是代理 Object exposedObject = bean; try { // bean初始化第二步:依赖注入 // 调用反射和内省去进行属性设置 // 属性值需要进行类型转换 populateBean(beanName, mbd, instanceWrapper); // bean初始化第三步:初始化,完成bean的初始化操作(AOP发生在此步骤) exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { // 从二级缓存中拿到先加载类的代理/本身 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 如果有循环引用,代理是会提前生成的,这就导致了先加载类在初始化时不再生成代理,也就导致了下面的判断成立 if (exposedObject == bean) { // 用代理替换,否则返回的是本身的Bean exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
再用图描述下解决循环引用的过程。
到此就解决了set方式下循环依赖的问题。
问题1:为什么构造器注入不能通过这种方式解决循环依赖?
答:因为构造器注入发生在实例化阶段,可以看到B在实例化时还没有去获取A的缓存的。
问题2:为什么要三级缓存呢?
答:我们不妨从结果看问题,三级缓存的是工厂,二级缓存的是Bean的代理。想想如果没有用到代理呢?那么二级缓存的就是Bean本身,这和三层缓存产生Bean的工厂实际是一样的,也就是在没有代理的情况,只需要二级缓存就足够了,三级缓存的目的是为了多一层来缓存代理。
问题3:三级缓存的存储时机?
答:一级缓存在Bean完成初始化之后存入,二级缓存在第一次从三级缓存的工厂中获取Bean后存入,三级缓存在Bean实例化完成后且存在循环依赖时存入。
参考:
1.Bean实例化方式:https://www.jianshu.com/p/646c1f657144,https://rumenz.com/java-topic/spring-core/spring-autowiring-by-constructor/index.html