Spring 순환 의존 을 해결 하려 면 3 급 캐 시 를 해 야 합 니까?
순환 의존
순환 의존 을 해결 하려 면 순환 의존 이 무엇 인지 알 아야 한다.다음 그림 에서 보 듯 이:
위의 그림 을 통 해 우 리 는 알 수 있다.
public class A {
private B b;
}
public class B {
private C c;
}
public class C {
private A a;
}
이런 의존 관 계 는 일종 의 폐쇄 적 인 고 리 를 형성 하여 순환 의존 국면 을 조성 했다.다음은 순환 의존 을 해결 하지 못 한 일반적인 절차 입 니 다.
Spring 순환 의존 해결 의 핵심 은 조기 노출 대상 이 고,조기 노출 대상 은 2 급 캐 시 에 두 는 것 이다.다음 표 는 3 급 캐 시 에 대한 설명 입 니 다.
명칭.
묘사 하 다.
singletonObjects
1 급 캐 시,완전한 Bean 을 저장 합 니 다.
earlySingletonObjects
2 급 캐 시,미리 노출 된 Bean 을 저장 합 니 다.Bean 은 완전 하지 않 고 속성 주입 과 init 방법 을 수행 하지 못 했 습 니 다.
singletonFactories
3 급 캐 시 는 Bean 공장 에 저장 되 어 있 으 며 주로 Bean 을 생산 하여 2 급 캐 시 에 저장 합 니 다.
Spring 에서 관리 하 는 모든 Bean 은 최종 적 으로 singleton Objects 에 저 장 됩 니 다.이 안에 저 장 된 Bean 은 모든 생명 주 기 를 거 쳤 습 니 다(폐 기 된 생명 주 기 를 제외 하고).완전 하여 사용자 에 게 사용 할 수 있 습 니 다.
early Singleton Objects 는 실례 화 되 었 지만,init 방법 을 실행 하 는 Bean 을 주입 하지 않 았 습 니 다.
singleton Factory 는 Bean 을 생산 하 는 공장 을 맡 고 있 습 니 다.
빈 은 이미 실례 화 되 었 는데,왜 빈 을 생산 하 는 공장 이 필요 합 니까?여 기 는 사실상 AOP 와 관련 된 것 이다.만약 에 프로젝트 에서 Bean 을 위해 대리 할 필요 가 없다 면 이 Bean 공장 은 처음부터 정례 화 된 대상 으로 바로 돌아 갈 것 이다.만약 에 AOP 를 사용 하여 대리 할 필요 가 있다 면 이 공장 은 중요 한 역할 을 발휘 할 것 이다.이것 도 본 고 에서 중점적으로 주목 해 야 할 문제 중 하나 이다.
순환 의존 해결
Spring 은 위 에서 소개 한 3 급 캐 시 를 통 해 순환 의존 을 어떻게 해결 합 니까?여 기 는 A,B 로 형 성 된 순환 의존 만 예 를 들 면:
Bean 을 만 드 는 방법 은 Abstract AutowireCapableBean Factory:doCreate Bean()
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// ①
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null;
Class<?> beanType = instanceWrapper != null ? instanceWrapper.getWrappedClass() : null;
// ② , , ObjectFactory
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// ③
this.populateBean(beanName, mbd, instanceWrapper);
// ④ ,
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
3 급 캐 시 를 추가 하 는 방법 은 다음 과 같 습 니 다.
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) { //
this.singletonFactories.put(beanName, singletonFactory); //
this.earlySingletonObjects.remove(beanName); //
this.registeredSingletons.add(beanName);
}
}
}
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
이 코드 를 통 해 우 리 는 Spring 이 실례 화 대상 이 된 후에 빈 공장 을 만 들 고 이 공장 을 3 급 캐 시 에 넣 을 것 이라는 것 을 알 수 있다.따라서 스프링 이 처음에 미리 노출 된 것 은 정례 화 된 빈 이 아니 라 빈 을 포장 한 오 브 젝 트 팩 토리 다.왜 그 랬 을 까?
이 는 실제로 AOP 와 관련 된 것 으로,만 든 Bean 이 에이전트 가 있다 면 원본 Bean 이 아 닌 에이전트 Bean 을 주입 해 야 한다.그러나 Spring 은 처음에 Bean 이 순환 의존 이 있 는 지 알 지 못 했 습 니 다.일반적인 상황 에서(순환 의존 이 없 는 경우)Spring 은 속성 을 채 우 고 초기 화 방법 을 실행 한 후에 대 리 를 만 듭 니 다.그러나 순환 의존 이 나타 나 면 Spring 은 프 록 시 대상 을 미리 만들어 야 합 니 다.그렇지 않 으 면 프 록 시 대상 이 아 닌 원시 대상 을 주입 해 야 합 니 다.그래서 대리 대상 을 어디서 미리 만들어 야 하 는가?
Spring 의 방법 은 Object Factory 에서 대리 대상 을 미리 만 드 는 것 이다.빈 을 얻 기 위해 getObject()방법 을 실행 합 니 다.실제로 그것 이 진정 으로 실행 하 는 방법 은 다음 과 같다.
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// , ;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
프 록 시 를 미리 진행 하 였 기 때문에 나중에 프 록 시 대상 을 반복 적 으로 만 들 지 않도록 early Proxy References 에 대 리 된 대상 을 기록 합 니 다.
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
위의 분석 을 통 해 우 리 는 Spring 이 3 급 캐 시 를 필요 로 하 는 목적 은 순환 의존 이 없 는 상황 에서 대리 대상 의 생 성 을 지연 시 키 고 Bean 의 생 성 을 Spring 의 디자인 원칙 에 부합 하도록 하 는 것 임 을 알 수 있다.어떻게 의존 을 얻 습 니까?
우 리 는 스프링 의 3 급 의존 작용 을 이미 알 고 있 지만,스프링 은 속성 을 주입 할 때 어떻게 의존 을 얻 습 니까?
그 는 getSingleton()방법 을 통 해 필요 한 Bean 을 얻 었 다.
protected 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) {
// Bean Bean
singletonObject = singletonFactory.getObject();
//
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
Spring 이 한 Bean 에 속성 을 채 울 때 먼저 주입 대상 의 이름 을 찾 은 다음 getSingleton()방법 을 순서대로 실행 하여 주입 대상 을 얻 습 니 다.대상 을 가 져 오 는 과정 은 1 급 캐 시 에서 가 져 오 는 것 입 니 다.1 급 캐 시 에 없 으 면 2 급 캐 시 에서 가 져 오고 2 급 캐 시 에 없 으 면 3 급 캐 시 에서 가 져 옵 니 다.3 급 캐 시 에 도 없 으 면 doCreate Bean()방법 으로 이 Bean 을 만 듭 니 다.2 급 캐 시
세 번 째 캐 시 는 프 록 시 대상 의 생 성 을 지연 시 키 기 위 한 목적 임 을 알 고 있 습 니 다.순환 에 의존 하지 않 으 면 프 록 시 를 미리 만 들 필요 가 없 기 때문에 초기 화 완료 후 만 들 수 있 습 니 다.
목적 이 지연 되 는 것 이 라면 우 리 는 생 성 을 지연 시 키 지 않 고 정례 화 된 후에 프 록 시 대상 을 만 들 수 있 는 것 이 아 닙 니까?그러면 우 리 는 3 급 캐 시 를 필요 로 하지 않 습 니 다.따라서 우 리 는 addSingleton Factory()방법 을 개조 할 수 있다.
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) { //
object o = singletonFactory.getObject(); // Bean
this.earlySingletonObjects.put(beanName, o); //
this.registeredSingletons.add(beanName);
}
}
}
이렇게 되면 빈 을 예화 한 후 프 록 시 대상 을 만 들 고 2 급 캐 시 에 추가 합 니 다.테스트 결 과 는 완전히 정상 입 니 다.Spring 의 초기 화 시간 도 큰 영향 을 미 치지 않 을 것 입 니 다.Bean 자체 가 대리 가 필요 하지 않 으 면 원본 Bean 으로 직접 돌아 가 는 것 이기 때문에 복잡 한 대리 Bean 을 만 드 는 절 차 를 밟 을 필요 가 없습니다.결론.
테스트 에 의 하면 2 급 캐 시도 순환 의존 을 해결 할 수 있다 는 것 이 증명 되 었 다.왜 Spring 은 2 급 캐 시 를 선택 하지 않 고 캐 시 를 한 층 더 추가 해 야 합 니까?
Spring 이 순환 의존 을 해결 하기 위해 2 급 캐 시 를 선택 하면 모든 Bean 이 예화 가 완 료 된 후에 바로 대 리 를 만들어 야 한 다 는 것 을 의미 합 니 다.Spring 의 디자인 원칙 은 Bean 초기 화가 완 료 된 후에 야 대 리 를 만 드 는 것 입 니 다.그래서 Spring 은 3 급 캐 시 를 선 택 했 습 니 다.그러나 순환 의존 이 생 겨 서 Spring 은 프 록 시 를 미리 만 들 수 밖 에 없 었 습 니 다.프 록 시 대상 을 미리 만 들 지 않 으 면 원본 대상 을 주입 하면 오류 가 발생 할 수 있 기 때 문 입 니 다.
Spring 순환 의존 을 해결 하려 면 3 급 캐 시 를 해 야 합 니까? 순환 의존 3 급 캐 시 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부탁드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[MeU] Hashtag 기능 개발➡️ 기존 Tag 테이블에 존재하지 않는 해시태그라면 Tag , tagPostMapping 테이블에 모두 추가 ➡️ 기존에 존재하는 해시태그라면, tagPostMapping 테이블에만 추가 이후에 개발할 태그 기반 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.