Spring循环依赖问题,循环依赖的情况,能解决的情况,怎么解决的
目录
Spring中依赖注入的方式
1、构造器注入
2、setter方法注入
3、接口注入
循环依赖的种类
1、多例模式下的循环注入
2、单例模式下的setter注入
3、单例模式下的构造器注入
Spring能解决哪些循环依赖
多例模式下的循环注入,会导致无限创建对象,最终导致OOM,无法解决
Spring只能解决单例模式下的setter注入导致的循环依赖
单例模式下的构造器注入导致的循环依赖,在实例化对象时就出问题了,先实例化A,还是先实例化B,无法解决。
单例模式下的构造器注入循环依赖问题Spring无法解决
这种情况Spring无法解决
@Component
public class ClassA {
private ClassB classB;
@Autowired
public ClassA(ClassB classB) {
this.classB = classB;
}
public ClassB getClassB() {
return classB;
}
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
@Component
public class ClassB {
private ClassA classA;
@Autowired
public ClassB(ClassA classA) {
this.classA = classA;
}
public ClassA getClassA() {
return classA;
}
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
报错信息
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-06-13 21:06:57.115 ERROR 11652 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| classA defined in file [F:\javaProject\githubProject\spring-demo\target\classes\com\spring\springdemo\service\ClassA.class]
↑ ↓
| classB defined in file [F:\javaProject\githubProject\spring-demo\target\classes\com\spring\springdemo\service\ClassB.class]
└─────┘
Process finished with exit code 1
单例模式下的接口注入循环依赖问题Spring能解决
@Component
public class ClassA {
@Autowired
private ClassB classB;
public ClassB getClassB() {
return classB;
}
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
@Component
public class ClassB {
@Autowired
private ClassA classA;
public ClassA getClassA() {
return classA;
}
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
单例模式下的setter注入循环依赖问题Spring能解决
@Component
public class ClassA {
private ClassB classB;
public ClassB getClassB() {
return classB;
}
@Autowired
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
@Component
public class ClassB {
private ClassA classA;
public ClassA getClassA() {
return classA;
}
@Autowired
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
多例模式下的循环依赖Spring无法解决
VM参数设置堆大小为一个较小值:-Xmx5m
@Component
@Scope("prototype")
public class ClassA {
private ClassB classB;
public ClassB getClassB() {
return classB;
}
@Autowired
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
@Component
@Scope("prototype")
public class ClassB {
private ClassA classA;
public ClassA getClassA() {
return classA;
}
@Autowired
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
报错信息如下,这个错误是由于JVM花费太长时间执行GC且只能回收很少的堆内存时抛出的,因为创建了大量对象,而这些对象互相持有导致不能被GC回收。
Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethod(Class.java:2128)
at java.io.ObjectStreamClass.getPrivateMethod(ObjectStreamClass.java:1629)
at java.io.ObjectStreamClass.access$1700(ObjectStreamClass.java:79)
at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:520)
at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:494)
at java.security.AccessController.doPrivileged(Native Method)
at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:494)
at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:391)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1134)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
at java.lang.Throwable.writeObject(Throwable.java:985)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1140)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:396)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
2021-06-13 21:31:41.137 WARN 4956 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.spring.springdemo.SpringDemoApplication]; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
2021-06-13 21:31:41.287 INFO 4956 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-06-13 21:31:41.585 ERROR 4956 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.spring.springdemo.SpringDemoApplication]; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:610) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:111) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812) ~[spring-context-5.3.7.jar:5.3.7]
at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_211]
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:193) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:337) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) [spring-boot-2.5.0.jar:2.5.0]
at com.spring.springdemo.SpringDemoApplication.main(SpringDemoApplication.java:10) [classes/:na]
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
Process finished with exit code 1
Spring怎么解决的单例模式下的setter方法依赖注入引起的循环依赖问题
首先看下Spring三级缓存介绍
一个对象同一时间只能存储在其中一个缓存中。
1、一级缓存:singletonObjects
主要用于存储单例模式下创建完毕的Bean实例,包括该Bean的实例化,依赖注入,实例化等都完成的一个完整的Bean。
该缓存是对外使用的,也就是使用Spring框架的人。
2、二级缓存:earlySingletonObjects
用于存储单例模式下创建中的Bean实例,该Bean被提前暴露的引用
该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
为了解决第一个classA引用最终如何替换为代理对象的问题(如果有代理对象)
3、三级缓存:singletonFactories
用于存储单例模式下创建中的Bean实例,该Bean被提前暴露的引用
该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
此缓存是解决循环依赖最大的功臣
getBean方法从缓存中获取实例的源码
获取缓存的顺序:一级缓存——>二级缓存——>三级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
上一篇: 沙眼早期有哪些常见症状 沙眼初期症状介绍