微网站建设教程,云空间的网站,seo外贸仿牌网站换域名,关键词工具有哪些spring三级缓存以及Async产生循环引用spring三级缓存介绍三级缓存解除循环引用原理源码对应1、获取A#xff0c;从三级缓存中获取#xff0c;没有获取到2、构造A#xff0c;将A置入三级缓存构造A(创建A实例)置入缓存3、注入属性#xff0c;构造B扫描缓存实例的相关信息注入…
spring三级缓存以及Async产生循环引用spring三级缓存介绍三级缓存解除循环引用原理源码对应1、获取A从三级缓存中获取没有获取到2、构造A将A置入三级缓存构造A(创建A实例)置入缓存3、注入属性构造B扫描缓存实例的相关信息注入属性4、获取B从三级缓存中没有获取到5、构造B将B置入三级缓存6、注入属性构造A7、获取A三个缓存中三级缓存命中8、创建B实例后续步骤9、B构造完成实例放入一级缓存二级三级缓存移除10、创建A实例后续步骤11、A构造完成实例放入一级缓存二级三级缓存移除Async导致三级缓存没有解除循环引用复现测试类描述备注启动测试原因分析解决方法参考spring三级缓存介绍
三级缓存解除循环引用原理 A B A 一级缓存 singletonObjects 二级缓存 earlySingletonObjects 三级缓存 singletonFactories 1、获取A从三级缓存中获取没有获取到 2、构造A将A置入三级缓存 3、注入属性构造B 4、获取B从三级缓存中没有获取到 5、构造B将B置入三级缓存 6、注入属性构造A 7、获取A从三级缓存中获取一级没有、二级没有、三级存在。此时通过从三级的BeanFactory构造实例对象放入二级缓存移除三级缓存 8、创建B实例后续步骤 9、B构造完成实例放入一级缓存二级三级缓存移除 10、创建A实例后续步骤 11、A构造完成实例放入一级缓存二级三级缓存移除 源码对应
springboot 2.5.6
1、获取A从三级缓存中获取没有获取到
// AbstractBeanFactory#doGetBean 256行
protected T T doGetBean(String name, Nullable ClassT requiredType, Nullable Object[] args, boolean typeCheckOnly)throws BeansException {
String beanName transformedBeanName(name);
Object beanInstance;// 从三个缓存中依次获取注意方法点进去allowEarlyReference是true
Object sharedInstance getSingleton(beanName);...2、构造A将A置入三级缓存
构造A(创建A实例)
// AbstractAutowireCapableBeanFactory#doCreateBean 582行
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args)throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper null;
if (mbd.isSingleton()) {instanceWrapper this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper null) {
// 这里会通过构造函数创建Bean实例instanceWrapper createBeanInstance(beanName, mbd, args);
}
//省略代码...置入缓存
// AbstractAutowireCapableBeanFactory#doCreateBean 613行
//省略代码...
boolean earlySingletonExposure (mbd.isSingleton() this.allowCircularReferences isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace(Eagerly caching bean beanName to allow for resolving potential circular references);}// 这里会把生成beanName的BeanFactory置入三级缓存addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean));
}
//省略代码...3、注入属性构造B
扫描缓存实例的相关信息 InitDestroyAnnotationBeanPostProcessor会解析缓存类中PostStruct和PreDestroy等相关信息 CommonAnnotationBeanPostProcessor会解析缓存类中Recource、WebServiceRef、PostStruct和PreDestroy等相关信息 AutowiredAnnotationBeanPostProcessor会解析缓存类中Autowired、Value、Inject等相关信息 // AbstractAutowireCapableBeanFactory#doCreateBean 594行
//省略代码...
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;}
}
//省略代码...
注入属性
// AbstractAutowireCapableBeanFactory#doCreateBean 619行
//省略代码...
try {// 这里会将注入对应的属性populateBean(beanName, mbd, instanceWrapper);exposedObject initializeBean(beanName, exposedObject, mbd);
}
//省略代码...// AbstractAutowireCapableBeanFactory#populateBean 1431行
//省略代码...
// 这里会依次遍历 如果是Resource会在CommonAnnotationBeanPostProcessor中触发注入
// 如果是Autowired则会在AutowiredAnnotationBeanPostProcessor中触发注入
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {PropertyValues pvsToUse bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse null) {if (filteredPds null) {filteredPds filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse null) {return;}}pvs pvsToUse;
}
//省略代码...4、获取B从三级缓存中没有获取到
5、构造B将B置入三级缓存
6、注入属性构造A 上面三个其实类似同前面三步骤差不多 7、获取A三个缓存中三级缓存命中 // AbstractBeanFactory#doGetBean 256行 入口
// DefaultSingletonBeanRegistry#getSingleton 150
// 此时一级缓存和二级缓存里面都没有三级缓存在第二步里面放进去了此时能拿到对应的ObjectFactory
//通过它可以获取对应的实例。然后将实例放入二级缓存将三级缓存中对应的BeanName移除
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject this.singletonObjects.get(beanName);if (singletonObject null isSingletonCurrentlyInCreation(beanName)) {singletonObject this.earlySingletonObjects.get(beanName);if (singletonObject null allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject this.singletonObjects.get(beanName);if (singletonObject null) {singletonObject this.earlySingletonObjects.get(beanName);if (singletonObject null) {ObjectFactory? singletonFactory this.singletonFactories.get(beanName);if (singletonFactory ! null) {singletonObject singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}8、创建B实例后续步骤
Async循环引用触发点 这里属性注入完后剩下bean增强、循环依赖校验、注册DisposableBean 9、B构造完成实例放入一级缓存二级三级缓存移除
// DefaultSingletonBeanRegistry#getSingleton 259行
//省略代码...
// 这在执行链路的上面几层。这里判断是否是新的单例如果是则置入缓存(一级缓存)中
if (newSingleton) {addSingleton(beanName, singletonObject);
}
//省略代码...protected void addSingleton(String beanName, Object singletonObject) {
// 放入一级缓存从二级缓存和三级缓存中移除synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}
10、创建A实例后续步骤
11、A构造完成实例放入一级缓存二级三级缓存移除 逻辑基本同上 Async导致三级缓存没有解除循环引用
复现
测试类
Service
Slf4j
public class AService {static {log.info(AService init);}Resourceprivate BService bService;public String a2bService(){return bService.get();}Async(async-2)public void exec(){log.info(aService exec);}}Service
Slf4j
public class BService {static {log.info(BService init);}Autowiredprivate AService aService;public String get(){return BService 666;}public void exec(){log.info(BService exec);}}描述 这里AService 和BService在同一个目录下也没有被在别的类中注入(如果有一个类把两者都注入了且Bservice先注入此时如果三个类中该类最先注入就会导致Bservice先初始化)。在这种情况下默认就会Aservice先初始化然后在Bservice初始化如此才能复现问题。 如果想要调整类加载顺序可以通过DependsOn(value AService)或者Lazy 备注 别开启懒加载不然启动不会报错。 spring.main.lazy-initializationtrue 启动测试
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name AService: Bean with name AService has been injected into other beans [BService] 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 getBeanNamesForType with the allowEagerInit flag turned off, for example.at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:649)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)at org.springframework.boot.SpringApplication.run(SpringApplication.java:338)at com.yichen.casetest.CaseTestApplication.main(CaseTestApplication.java:47)原因分析
在bean实例后续步骤中会对bean增强
// AbstractAutowireCapableBeanFactory#doCreateBean 620行
try {populateBean(beanName, mbd, instanceWrapper);// 初始化bean对Bean增强// 1、invokeAwareMethods// 2、applyBeanPostProcessorsBeforeInitialization// 3、invokeInitMethods// 4、applyBeanPostProcessorsAfterInitializationexposedObject initializeBean(beanName, exposedObject, mbd);
}在applyBeanPostProcessorsAfterInitialization中由于使用了异步线程池(用了EnableAsync)使得AsyncAnnotationBeanPostProcessor注入了spring容器它会为原有的bean实例创建CGLIB代理使得最初的bean和实例化后暴露出去的bean不是同一个没有通过循环引用校验抛出了异常。 // AsyncAnnotationBeanPostProcessor的子类
//AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization 86行
// 如果类中有Async会创建代理
if (isEligible(bean, beanName)) {ProxyFactory proxyFactory prepareProxyFactory(bean, beanName);if (!proxyFactory.isProxyTargetClass()) {evaluateProxyInterfaces(bean.getClass(), proxyFactory);}proxyFactory.addAdvisor(this.advisor);customizeProxyFactory(proxyFactory);// Use original ClassLoader if bean class not locally loaded in overriding class loaderClassLoader classLoader getProxyClassLoader();if (classLoader instanceof SmartClassLoader classLoader ! bean.getClass().getClassLoader()) {classLoader ((SmartClassLoader) classLoader).getOriginalClassLoader();}return proxyFactory.getProxy(classLoader);
}// AbstractAutowireCapableBeanFactory#doCreateBean 632行
// 循环引用检测
if (earlySingletonExposure) {Object earlySingletonReference getSingleton(beanName, false);if (earlySingletonReference ! null) {if (exposedObject bean) {exposedObject earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping hasDependentBean(beanName)) {String[] dependentBeans getDependentBeans(beanName);SetString 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 getBeanNamesForType with the allowEagerInit flag turned off, for example.);}}}
}解决方法
1、不用异步线程池自己实现线程池 2、通过lazy、DependsOn调整类加载顺序如果让Bservice先加载就不会出错 3、梳理业务逻辑调整技术实现让AService和Bservice不循环引用
参考
Using Async in a circular dependency will throw a BeanCurrentlyInCreationException