若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效。或许刚说到这,有的小伙伴就会大惊失色了。Spring
不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让咱们一块儿揭秘Spring
循环依赖的最本质缘由。java
protected Object doCreateBean( ... ){ ... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } ... // populateBean这一句特别的关键,它须要给A的属性赋值,因此此处会去实例化B~~ // 而B咱们从上能够看到它就是个普通的Bean(并不须要建立代理对象),实例化完成以后,继续给他的属性A赋值,而此时它会去拿到A的早期引用 // 也就在此处在给B的属性a赋值的时候,会执行到上面放进去的Bean A流程中的getEarlyBeanReference()方法 从而拿到A的早期引用~~ // 执行A的getEarlyBeanReference()方法的时候,会执行自动代理建立器,可是因为A没有标注事务,因此最终不会建立代理,so B合格属性引用会是A的**原始对象** // 须要注意的是:@Async的代理对象不是在getEarlyBeanReference()中建立的,是在postProcessAfterInitialization建立的代理 // 从这咱们也能够看出@Async的代理它默认并不支持你去循环引用,由于它并无把代理对象的早期引用提供出来~~~(注意这点和自动代理建立器的区别~) // 结论:此处给A的依赖属性字段B赋值为了B的实例(由于B不须要建立代理,因此就是原始对象) // 而此处实例B里面依赖的A注入的仍旧为Bean A的普通实例对象(注意 是原始对象非代理对象) 注:此时exposedObject也依旧为原始对象 populateBean(beanName, mbd, instanceWrapper); // 标注有@Async的Bean的代理对象在此处会被生成~~~ 参照类:AsyncAnnotationBeanPostProcessor // 因此此句执行完成后 exposedObject就会是个代理对象而非原始对象了 exposedObject = initializeBean(beanName, exposedObject, mbd); ... // 这里是报错的重点~~~ if (earlySingletonExposure) { // 上面说了A被B循环依赖进去了,因此此时A是被放进了二级缓存的,因此此处earlySingletonReference 是A的原始对象的引用 // (这也就解释了为什么我说:若是A没有被循环依赖,是不会报错不会有问题的 由于若没有循环依赖earlySingletonReference =null后面就直接return了) Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 上面分析了exposedObject 是被@Aysnc代理过的对象, 而bean是原始对象 因此此处不相等 走else逻辑 if (exposedObject == bean) { exposedObject = earlySingletonReference; } // allowRawInjectionDespiteWrapping 标注是否容许此Bean的原始类型被注入到其它Bean里面,即便本身最终会被包装(代理) // 默认是false表示不容许,若是改成true表示容许,就不会报错啦。这是咱们后面讲的决方案的其中一个方案~~~ // 另外dependentBeanMap记录着每一个Bean它所依赖的Bean的Map~~~~ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { // 咱们的Bean A依赖于B,so此处值为["b"] String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); // 对全部的依赖进行一一检查~ 好比此处B就会有问题 // “b”它通过removeSingletonIfCreatedForTypeCheckOnly最终返返回false 由于alreadyCreated里面已经有它了表示B已经彻底建立完成了~~~ // 而b都完成了,因此属性a也赋值完成儿聊 可是B里面引用的a和主流程我这个A居然不相等,那确定就有问题(说明不是最终的)~~~ // so最终会被加入到actualDependentBeans里面去,表示A真正的依赖~~~ 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."); } } } } ... }
Object earlySingletonReference = getSingleton(beanName, false);
确定有值addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
将给实例对象添加SmartInstantiationAwareBeanPostProcessor
AbstractAutoProxyCreator
是SmartInstantiationAwareBeanPostProcessor
的子类,必定记住了,必定记住,SmartInstantiationAwareBeanPostProcessor
的子类很关键!!!!!exposedObject = initializeBean(beanName, exposedObject, mbd);
进行BeanPostProcessor
后置处理,注意是BeanPostProcessor
!!!!!Spring
的循环依赖被它的三级缓存给轻易解决了,可是这2个地方的后置处理带来了 循环依赖的问题。程序员
对比AbstractAdvisorAutoProxyCreator和AsyncAnnotationBeanPostProcessor编程
因为SmartInstantiationAwareBeanPostProcessor
的子类会在两处都会执行后置处理,因此先后都会相同的对象引用,不会发生循环依赖问题,异步注解就不行了 ,至于为何?本身看上面的分析,仔细看哦!缓存
@Lazy
注解allowRawInjectionDespiteWrapping
设置为true
(利用了判断的那条语句)BeanPostProcessor
设计到的注解,,哈哈 这不太现实。 @Lazy
通常含义是懒加载,它只会做用于BeanDefinition.setLazyInit()
。而此处给它增长了一个能力:延迟处理(代理处理)app
// @since 4.0 出现得挺晚,它支持到了@Lazy 是功能最全的AutowireCandidateResolver public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver { // 这是此类自己惟一作的事,此处精析 // 返回该 lazy proxy 表示延迟初始化,实现过程是查看在 @Autowired 注解处是否使用了 @Lazy = true 注解 @Override @Nullable public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { // 若是isLazy=true 那就返回一个代理,不然返回null // 至关于若标注了@Lazy注解,就会返回一个代理(固然@Lazy注解的value值不能是false) return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null); } // 这个比较简单,@Lazy注解标注了就行(value属性默认值是true) // @Lazy支持标注在属性上和方法入参上~~~ 这里都会解析 protected boolean isLazy(DependencyDescriptor descriptor) { for (Annotation ann : descriptor.getAnnotations()) { Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class); if (lazy != null && lazy.value()) { return true; } } MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { Method method = methodParam.getMethod(); if (method == null || void.class == method.getReturnType()) { Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class); if (lazy != null && lazy.value()) { return true; } } } return false; } // 核心内容,是本类的灵魂~~~ protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) { Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory, "BeanFactory needs to be a DefaultListableBeanFactory"); // 这里绝不客气的使用了面向实现类编程,使用了DefaultListableBeanFactory.doResolveDependency()方法~~~ final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory(); //TargetSource 是它实现懒加载的核心缘由,在AOP那一章节了重点提到过这个接口,此处再也不叙述 // 它有不少的著名实现如HotSwappableTargetSource、SingletonTargetSource、LazyInitTargetSource、 //SimpleBeanTargetSource、ThreadLocalTargetSource、PrototypeTargetSource等等很是多 // 此处由于只须要本身用,因此采用匿名内部类的方式实现~~~ 此处最重要是看getTarget方法,它在被使用的时候(也就是代理对象真正使用的时候执行~~~) TargetSource ts = new TargetSource() { @Override public Class<?> getTargetClass() { return descriptor.getDependencyType(); } @Override public boolean isStatic() { return false; } // getTarget是调用代理方法的时候会调用的,因此执行每一个代理方法都会执行此方法,这也是为什么doResolveDependency // 我我的认为它在效率上,是存在必定的问题的~~~因此此处建议尽可能少用@Lazy~~~ //不过效率上应该还好,对比http、序列化反序列化处理,简直不值一提 因此仍是无所谓 用吧 @Override public Object getTarget() { Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null); if (target == null) { Class<?> type = getTargetClass(); // 对多值注入的空值的友好处理(不要用null) if (Map.class == type) { return Collections.emptyMap(); } else if (List.class == type) { return Collections.emptyList(); } else if (Set.class == type || Collection.class == type) { return Collections.emptySet(); } throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(), "Optional dependency not present for lazy injection point"); } return target; } @Override public void releaseTarget(Object target) { } }; // 使用ProxyFactory 给ts生成一个代理 // 因而可知最终生成的代理对象的目标对象实际上是TargetSource,而TargetSource的目标才是咱们业务的对象 ProxyFactory pf = new ProxyFactory(); pf.setTargetSource(ts); Class<?> dependencyType = descriptor.getDependencyType(); // 若是注入的语句是这么写的private AInterface a; 那这类就是借口 值是true // 把这个接口类型也得放进去(否则这个代理都不属于这个类型,反射set的时候岂不直接报错了吗????) if (dependencyType.isInterface()) { pf.addInterface(dependencyType); } return pf.getProxy(beanFactory.getBeanClassLoader()); } }
标注有@Lazy
注解完成注入的时候,最终注入只是一个此处临时生成的代理对象,只有在真正执行目标方法的时候才会去容器内拿到真是的bean
实例来执行目标方法。异步
利用allowRawInjectionDespiteWrapping属性来强制改变判断ide
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true); } }
这样会致使容器里面的是代理对象,暴露给其余实例的是原始引用,致使不生效了。因为它只对循环依赖内的Bean
受影响,因此影响范围并非全局,所以当找不到更好办法的时候,此种这样也不失是一个不错的方案。源码分析