Spring 순환 의존 해결 방법,정말 알 겠 어?

11538 단어 Spring순환 의존

소개 하 다.
먼저 순환 의존 이 무엇 인지,나 쁜 의존 즉 순환 인용,두 개 또는 여러 개의 bean 이 서로 인용 하여 최종 적 으로 하나의 링 을 형성한다.Spring 은 A 를 초기 화 할 때 B 를 주입 해 야 하고,B 를 초기 화 할 때 A 를 주입 해 야 하 며,Spring 이 시 작 된 후 이 두 개의 Bean 을 모두 초기 화 해 야 한다.

Spring 의 순환 의존 은 두 가지 장면 이 있 습 니 다.
구조 기의 순환 의존
  • 속성의 순환 의존
  • 구조 기의 순환 의존 은 구조 함수 에서@Lazy 주해 로 딩 지연 을 사용 할 수 있 습 니 다.주입 의존 시 프 록 시 대상 을 먼저 주입 하고 처음 사용 할 때 대상 을 만 들 고 주입 을 완료 합 니 다.
    속성의 순환 의존 은 주로 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 부분 으로 나 누 었 습 니 다.
  • bean 의 실례 화 과정,즉 구조 함 수 를 호출 하여 대상 을 만 듭 니 다
  • bean 의 초기 화 과정,즉 bean 의 각종 속성 을 채 우 는 것 입 니 다
  • bean 초기 화 과정 이 끝나 면 bean 은 정상적으로 생 성 될 수 있 습 니 다.
    다음은 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 순환 의존 을 분석 하 는 거의 모든 글 에서 언급 될 것 입 니 다.이번 에는 작업 원리 가 무엇 인지 알 겠 습 니 다.
    총 결 파
  • bean 을 가 져 올 때 singleton Objects(1 급 캐 시)에서 가 져 옵 니 다
  • 가 져 오지 못 하고 대상 이 만 들 고 있 으 면 early Singleton Objects(2 급 캐 시)에서 가 져 옵 니 다
  • 가 져 오지 못 하면 singleton Factory(3 급 캐 시)에서 가 져 온 다음 가 져 온 대상 을 early Singleton Objects(2 급 캐 시)에 넣 고 bean 에 대응 하 는 singleton Factory(3 급 캐 시)를 지 웁 니 다
  • bean 초기 화 완료,singletonObjects(1 급 캐 시)에 bean 에 대응 하 는 early SingletonObjects(2 급 캐 시)를 제거 합 니 다
  • 참고 블 로그
    [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 순환 의존 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 세 요.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기