Spring 순환 의존 해결 방법,정말 알 겠 어?
소개 하 다.
먼저 순환 의존 이 무엇 인지,나 쁜 의존 즉 순환 인용,두 개 또는 여러 개의 bean 이 서로 인용 하여 최종 적 으로 하나의 링 을 형성한다.Spring 은 A 를 초기 화 할 때 B 를 주입 해 야 하고,B 를 초기 화 할 때 A 를 주입 해 야 하 며,Spring 이 시 작 된 후 이 두 개의 Bean 을 모두 초기 화 해 야 한다.
Spring 의 순환 의존 은 두 가지 장면 이 있 습 니 다.
구조 기의 순환 의존
속성의 순환 의존 은 주로 3 개의 맵 을 통 해 해결 된다
구조 기의 순환 의존
@Component
public class ConstructorA {
private ConstructorB constructorB;
@Autowired
public ConstructorA(ConstructorB constructorB) {
this.constructorB = constructorB;
}
}
@Component
public class ConstructorB {
private ConstructorA constructorA;
@Autowired
public ConstructorB(ConstructorA constructorA) {
this.constructorA = constructorA;
}
}
@Configuration
@ComponentScan("com.javashitang.dependency.constructor")
public class ConstructorConfig {
}
public class ConstructorMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ConstructorConfig.class);
System.out.println(context.getBean(ConstructorA.class));
System.out.println(context.getBean(ConstructorB.class));
}
}
Constructor Main 의 main 방법 을 실행 할 때 첫 줄 에서 이상 을 보고 합 니 다.이 는 Spring 이 모든 Bean 을 초기 화 할 수 없다 는 것 을 의미 합 니 다.즉,위의 이러한 형식의 순환 은 Spring 에 의존 하여 해결 할 수 없습니다.저 희 는 ConstructorA 나 ConstructorB 구조 함수 의 매개 변수 에@Lazy 주 해 를 더 하면 해결 할 수 있 습 니 다.
@Autowired
public ConstructorB(@Lazy ConstructorA constructorA) {
this.constructorA = constructorA;
}
우 리 는 주로 속성의 순환 의존 에 관심 을 가지 기 때문에 구조 기의 순환 의존 은 너무 많은 분석 을 하지 않 는 다.속성의 순환 의존
속성 순환 의존 이 무엇 인지 먼저 보 여 주세요.
@Component
public class FieldA {
@Autowired
private FieldB fieldB;
}
@Component
public class FieldB {
@Autowired
private FieldA fieldA;
}
@Configuration
@ComponentScan("com.javashitang.dependency.field")
public class FieldConfig {
}
public class FieldMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(FieldConfig.class);
// com.javashitang.dependency.field.FieldA@3aa9e816
System.out.println(context.getBean(FieldA.class));
// com.javashitang.dependency.field.FieldB@17d99928
System.out.println(context.getBean(FieldB.class));
}
}
Spring 용기 가 정상적으로 작 동 하여 FieldA 와 FieldB 두 개의 Bean 을 얻 을 수 있 습 니 다.속성의 순환 은 면접 에서 자주 묻 는 질문 에 의존한다.전체적으로 말 하면 복잡 하지 않 지만 Spring Bean 의 초기 화 과정 과 관련 되 기 때문에 복잡 하 다 고 느 꼈 습 니 다.저 는 demo 를 써 서 전체 과정 을 보 여 드 리 겠 습 니 다.
Spring 의 Bean 의 초기 화 과정 은 사실 비교적 복잡 하 다.Demo 를 이해 하기 위해 저 는 Spring Bean 의 초기 화 과정 을 2 부분 으로 나 누 었 습 니 다.
다음은 Demo,Object Factory 인 터 페 이 스 를 사용 하여 Bean 을 생산 합 니 다.Spring 에서 정의 하 는 인터페이스 와 같 습 니 다.
public interface ObjectFactory<T> {
T getObject();
}
public class DependencyDemo {
// Bean
private final Map<String, Object> singletonObjects =
new ConcurrentHashMap<>(256);
// Bean ,
private final Map<String, ObjectFactory<?>> singletonFactories =
new HashMap<>(16);
// Bean,
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
public <T> T getBean(Class<T> beanClass) throws Exception {
// Bean
String beanName = beanClass.getSimpleName();
// ,
Object initObj = getSingleton(beanName, true);
if (initObj != null) {
return (T) initObj;
}
// bean
singletonsCurrentlyInCreation.add(beanName);
// bean
Object object = beanClass.getDeclaredConstructor().newInstance();
singletonFactories.put(beanName, () -> {
return object;
});
// bean,
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// class
Class<?> fieldClass = field.getType();
field.set(object, getBean(fieldClass));
}
//
singletonObjects.put(beanName, object);
singletonsCurrentlyInCreation.remove(beanName);
return (T) object;
}
/**
* allowEarlyReference Spring , true
* allowEarlyReference false , ,
*/
public Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null
&& isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
}
}
}
}
return singletonObject;
}
/**
* bean
*/
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
}
테스트 라운드
public static void main(String[] args) throws Exception {
DependencyDemo dependencyDemo = new DependencyDemo();
//
Class[] classes = {A.class, B.class};
// bean
for (Class aClass : classes) {
dependencyDemo.getBean(aClass);
}
// true
System.out.println(
dependencyDemo.getBean(B.class).getA() == dependencyDemo.getBean(A.class));
// true
System.out.println(
dependencyDemo.getBean(A.class).getB() == dependencyDemo.getBean(B.class));
}
쉽 죠?저희 가 맵 2 개 만 써 서 Spring 의 순환 의존 을 해 결 했 습 니 다.두 개의 맵 으로 순환 의존 을 해결 할 수 있 는데 왜 Spring 은 세 개의 맵 을 사용 합 니까?
그 이 유 는 간단 합 니 다.singletonFactory 에서 BeanName 에 따라 해당 하 는 Object Factory 를 얻 은 다음 getObject()라 는 방법 으로 대응 하 는 Bean 을 되 돌려 줍 니 다.우리 의 예 에서
ObjectFactory 의 실현 은 매우 간단 합 니 다.바로 실례 화 된 대상 을 직접 되 돌려 주 는 것 입 니 다.그러나 Spring 에 서 는 이렇게 간단 하지 않 습 니 다.실행 과정 이 복잡 합 니 다.매번 ObjectFactory 를 받 은 후에 getObject()를 호출 하지 않도록 ObjectFactory 가 만 든 대상 을 캐 시 하면 효율 을 높 일 수 있 습 니 다.
예 를 들 어 A 는 B 와 C 에 의존 하고 B 와 C 는 A 에 의존 하 며 캐 시 를 하지 않 으 면 B 와 C 를 초기 화하 면 A 에 대응 하 는 Object Factory 의 getObject()방법 을 호출 합 니 다.캐 시 를 만 들 려 면 B 나 C 가 한 번 만 호출 하면 됩 니 다.
생각 을 알 게 되 었 습 니 다.우 리 는 위의 코드 를 바 꾸 고 캐 시 를 추가 합 니 다.
public class DependencyDemo {
// Bean
private final Map<String, Object> singletonObjects =
new ConcurrentHashMap<>(256);
// Bean ,
private final Map<String, ObjectFactory<?>> singletonFactories =
new HashMap<>(16);
// Bean Bean
private final Map<String, Object> earlySingletonObjects =
new HashMap<>(16);
// Bean,
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
public <T> T getBean(Class<T> beanClass) throws Exception {
// Bean
String beanName = beanClass.getSimpleName();
// ,
Object initObj = getSingleton(beanName, true);
if (initObj != null) {
return (T) initObj;
}
// bean
singletonsCurrentlyInCreation.add(beanName);
// bean
Object object = beanClass.getDeclaredConstructor().newInstance();
singletonFactories.put(beanName, () -> {
return object;
});
// bean,
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// class
Class<?> fieldClass = field.getType();
field.set(object, getBean(fieldClass));
}
singletonObjects.put(beanName, object);
singletonsCurrentlyInCreation.remove(beanName);
earlySingletonObjects.remove(beanName);
return (T) object;
}
/**
* allowEarlyReference Spring , true
*/
public Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null
&& isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}
우리 가 쓴 getSingleton 의 실현 은 org.spring from work.beans.factory.support.Default Singleton Bean Registry\#getSingleton(java.lang.String,boolean)의 실현 과 똑 같 습 니 다.이 방법 은 Spring 순환 의존 을 분석 하 는 거의 모든 글 에서 언급 될 것 입 니 다.이번 에는 작업 원리 가 무엇 인지 알 겠 습 니 다.총 결 파
[1] https://mp.weixin.qq.com/s/gBr3UfC1HRcw4U-ZMmtRaQ
[2] https://mp.weixin.qq.com/s/5mwkgJB7GyLdKDgzijyvXw
비교적 상세 하 다
[1] https://zhuanlan.zhihu.com/p/84267654
[2] https://juejin.im/post/5c98a7b4f265da60ee12e9b2
Spring 순환 의존 에 관 한 이 해결 방법 을 알 겠 습 니까?글 은 여기까지 소개 되 었 습 니 다.더 많은 Spring 순환 의존 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 세 요.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[MeU] Hashtag 기능 개발➡️ 기존 Tag 테이블에 존재하지 않는 해시태그라면 Tag , tagPostMapping 테이블에 모두 추가 ➡️ 기존에 존재하는 해시태그라면, tagPostMapping 테이블에만 추가 이후에 개발할 태그 기반 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.