1. 서문
우리는 봄이로드가 게으를 수 있음을 알고 있습니다. 즉, 콩이 실제로 사용될 때 콩을 인스턴스화합니다. 물론 이것은 사실이 아닙니다. 예를 들어, Bean의 게으른 이니 니트 속성을 구성하면 스프링의 적재 시간을 제어 할 수 있습니다. 이제 기계의 성능, 메모리 등은 비교적 높으며 기본적으로 게으른 하중을 사용하지 않습니다. 콩은 컨테이너 시작시로드되며 시작 시간은 조금 더 길다. 이런 식으로, 콩이 실제로 비즈니스 사용을 위해 얻을 때 많은 부담을 줄일 수 있습니다. 이것은 나중에 분석됩니다. 우리가 콩을 사용할 때 가장 직접적인 방법은 Bean 인스턴스를 적재하는 원인 인 Factroy에서 가져 오는 것입니다.
최근에 프로젝트 작업을 수행 할 때 이상한 문제가 발생했습니다. 당신이 서둘러 있다면, 내가 당신에게 하나씩 말씀 드리겠습니다.
2. 주입 순서와 관련된 일반 Bean Cyclic Dependency
2.1 원형 의존성 예와 원리
Public Class Beana {private beanb beanb; public beanb getbeanb () {return beanb;} public void setbeanb (beanb beanb) {this.beanb = beanb;}} Public Class Beanb {private beana beana; public beana getbeana () {return beana;} public void setbeana (beana beana) {this.beana = beana;}}<bean id = "beana"> <property name = "beanb"> <ref bean = "beanb"/> </property> </bean>
<bean id = "beanb"> <property name = "beana"> <ref bean = "beana"/> </property> </bean>
위의 원형 의존성 주입은 Spring이 초기 비밀 회의 기능을 제공하기 때문에 정상적으로 작동합니다. 먼저, 봄에는 SingletonObjects라는 동시 맵이 모두 인스턴스화되고 초기화 된 콩을 저장하는 반면, SingletonFactories는 해결 해야하는 Bean Information (Beanname 및 Callback Factory)을 저장하는 데 사용됩니다. Beana를 인스턴스화 할 때 getBean(“beanA”); 먼저, SingletonObjects에 Beana가 있는지 확인하면 다음이 반환됩니다.
(1)
Object SharedInstance = GetSingleton (Beaname); // GetSingleton (Beanname, True); if (shareedInstance! = null && args == null) {if (logger.isdebugenabled ()) {if (issingletoncurrenty -sincreation (beanname)) + " '아직 완전히 초기화되지 않은 것은 - 원형 참조의 결과"); } else {logger.debug ( "싱글 톤 Bean의 캐시 된 인스턴스를 반환" " + beanname +" ""); }} // 일반 Bean 인 경우 sharedInstance.getObject (); bean = getObjectForBeanInstance (sharedInstance, name, beanname, null);}를 반환합니다. 보호 된 개체 GetSingleton (String Beanname, Boolean allowearlyReference) {Object SingletonObject = this.singletonObjects.get (BeanName); if (SingletonObject == null) {synchronized (this.singletonObjects) {SingletonObject = this.earlySingletonObjects.get (beanname); if (singletonObject == null && allowearLyReference) {ObjectFactory SingleTonFactory = (ObjectFactory) this.singletonFactory.get (beanname); if (SingletOnfactory! = null) {SingletonObject = singletonFactory.getObject (); this.earlysingletonobjects.put (Beanname, SingletonObject); this.singletonfactory.remove (beanname); }}} return (SingletonObject! = null_object? singletonobject : null);} 처음에는 BEANA가 없으므로 allowCircularReferences=true 가 설정되고 (기본값이 true) 현재 Bean이 단일 피스이고 Bean이 현재 생성되고있는 경우 속성을 초기화하기 전에 Bean 정보를 단일 피스 맵에 넣습니다.
(2)
부울 EarlySingletonexposure = (mbd.issingleton () && this.lowercircularReferences && issingletoncurrentyIncreation (beanname));
if (earlysingletonexposure) {if (logger.isdebugenabled ()) {logger.debug ( " + beanname +" '' + beanname + " ''잠재적 원형 참조를 해결할 수 있도록"} 추가 letonmame, new objectomecome () {getObjeption () {beanxexcome () beanxexcome () beanxexcome () getearlyBeanReference (Beanname, MBD, Bean);} 보호 된 void addsingletonfactory (String Beanname, ObjectFactory SingletonFactory) {assert.notnull (SingletonFactory, "SingletonFactory,"Singleton Factory는 NULL이되어서는 안됩니다 "); Synchronized (this.singletonObjects) {if (! this.singletonObjects.containskey (beanname)) {this. 싱글 톤); this.earlysingletonobjects.remove (beanname); this.registeredsingletons.add (beanname); }}} 그런 다음 BEAB를 인스턴스 속성에 주입하십시오. 속성을 주입 할 때 getBean(“beanB”) 과 BeanB가 SingletonObjects에 있지 않다는 것을 알게되면 BeanB를 인스턴스화 한 다음 SingletonFactories를 넣은 다음 Beana를 주입 한 다음 getBean(“beanA”); 현재 GetSingleton은 인스턴스형 Beana를 반환 할 것입니다. BeanB가 초기화 된 후 Beanb를 SingletonObjects에 추가 한 다음 반환 한 다음 Beana가 초기화하고 Beana를 SingletonObjects에 추가 한 다음 반환합니다.
2.2 루프 종속성을 허용하는 스위치
public class testcircle2 {private final static classPathxmlapplicationContext moduleContext; 개인 정적 테스트 테스트; static {moduleContext = new ClassPathXmlApplicationContext (new String [] { "Beans-Circile.xml"}); modulecontext.setallowcircularReferences (false); test = (test) modulecontext.getbean ( "test");} public static void main (String [] args) {System.out.println (test.name);}}ClassPathXmlApplicationContext 클래스에는 원형 의존성이 기본적으로 true가 허용되는지 여부를 제어 할 수있는 속성 허용이 있습니다. False로 설정 한 후에는 원형 의존성이 여전히 정상적으로 실행될 수 있습니다. 소스 코드보기 :
public classPathXmlApplicationContext (string [] configlocations)는 beansexception {this (configlocations, true, null);} public classPathXmlApplicationContext (String [] configlocations, boolean Refresh, ApplicationContext Parent)가 beansexception {super (parent); setConfigLocations (configlocations); if (refresh ();}} 던지기 public classPathXmlApplicationContext (String [] configlocations, boolean Refresh, ApplicationContext Parent)가 beansexception {super (parent); setConfigLocations (configlocations); if (refresh ();}} 던지기 ClassPathXmlApplicationContext가 기본적으로 구성되면 컨테이너가 새로 고침 될 것임을 알고 있습니다.
새로 고침 방법은 RefreshBeanFactory를 호출합니다.
보호 된 최종 void refreshBeanFactory ()는 beansexception {if (hasbeanFactory ()) {DestroyBeans (); CloseBeanFactory ();} try {// 생성 Bean Factory defaultListableBeanFactory BeanFactory = CreateBeanFactory (); // Bean Factory Properties 사용자 정의 CustomizeBeanFactory (BeanFactory); loadBeendefinitions (beanfactory); 동기화 (this.beanfactorymonitor) {this.beanfactory = beanfactory; }} catch (ioexception ex) {throw new applicationcontextexception ( "I/O 오류 파싱 애플리케이션 컨텍스트에 대한 XML 문서 [" + getDisplayName () + "]", ex);}} 보호 된 void CustomizeBeanFactory (defaultListableBeanFactory BeanFactory) {if (this.xtualBeanDefinitionOverRiding! = null) {beanFactory.SetallowbeAndeFinitionOverRiding (this.combeanDefinitionOverriding.booleanValue ());} if (this. BeanCirce.SetallowCircularReferences (this.allowCircularReferences.booleanValue ());}} 여기서 당신은 moduleContext.setAllowCircularReferences(false) 호출하기 전에 스프링으로 남은 CustomizeBeanFactory가 실행되었음을 알 수 있습니다. 마지막 이유는 설정을 호출하기 전에 Bean Factory가 새로 고쳐서 테스트 코드가 다음으로 변경되기 때문입니다.
public class testcircle {private final static classxmlapplicationcontext modulecontext; private static test test; static {// 컨테이너 컨텍스트를 초기화하지만 컨테이너 modulecontext = new ClassPathXmlApplicationContext를 새로 고치지 마십시오. modulecontext.setallowcircularReferences (false); // 컨테이너 moduleContext.Refresh ()를 새로 고침합니다. test = (test) modulecontext.getbean ( "test");} public static void main (String [] args) {System.out.println (test.name);}}이제 테스트는 예외를 던집니다.
원인 : org.springframework.bean.beans.factory.beancreationException : 클래스 경로 리소스에서 정의 된 'Beana'이름으로 Bean을 생성하는 오류 [Beans-Circile.xml] : Bean Property 'Beanb'를 설정하는 동안 Bean Beanb '에 대한 참조를 해결할 수 없습니다. 중첩 예외는 org.springframework.bean.beans.factory.beancreationException : 클래스 경로 리소스에서 정의 된 'Beanb'이름으로 Bean을 생성하는 오류 [Beans-Circile.xml] : Bean 속성 'Beana'를 설정하는 동안 Bean 'Beana'에 대한 참조를 해결할 수 없습니다. 중첩 된 예외는 org.springframework.beans.fackory.beancurrentyincreationException : 'Beana'라는 이름으로 Bean을 생성하는 오류 : 요청 된 Bean은 현재 생성 중입니다. 해결할 수없는 원형 참조가 있습니까?
3. 공장 콩 및 일반 콩 주기적 종속성 - 주입 순서와 관련
3.1 테스트 코드
공장 콩
공개 클래스 MyFactoryBean은 FactoryBean, 초기화 이름; 개인 문자열 이름; 개인 테스트 테스트; public String getName () {return name;} public void setName (string name) {this.name = name;} public feleventbean getDepentBean () {ReturnEdentBean;} public setDepentBean (public setDepentBean) {thisheantbean). frivalEntbean;} private feencebean dependentBean; public object getObject ()는 예외 {return test;} 공개 클래스 getObjectType () {// todo 자동 생성 메소드 스터브 리턴 테스트 .class;} public boolean issingleton () {// to auto-auto-Generated Method return; System.out.println ( "이름 :" + this.name); test = new test (); test.name = sexendentBean.dosomething () + this.name;}} 단순화하기 위해 공개 변수를 작성하십시오
공개 수업 테스트 {공개 문자열 이름;} public class dependentbean {public String dosomething () {return "hello :";}@autowiredprivate test test;} XML 구성
<bean id = "test"> <property name = "fexendentBean"> </bean> </property> <property name = "name"value = "zlx"> </property> </bean>
Factory Bean myFactoryBean 기능은 테스트 클래스를 마무리하는 것입니다. 먼저, myFactoryBean의 속성을 설정 한 다음 MyFactoryBean의 AfterProperTiesset 메소드에서 테스트 인스턴스를 작성하고 속성을 설정하십시오. MyFactoryBean을 인스턴스화하면 결국 GetObject 메소드를 호출하여 생성 된 테스트 객체를 반환합니다. 여기서 MyFactoryBean은 DePentbean에 의존하고 FeendentBean 자체는 테스트에 따라 다르므로 이것은 원형 의존성입니다.
시험:
public class testcircle2 {private final static classPathxmlapplicationContext moduleContext; 개인 정적 테스트 테스트; static {moduleContext = new ClassPathXmlApplicationContext (new String [] { "Beans-Circile.xml"}); test = (test) modulecontext.getbean ( "test");} public static void main (String [] args) {System.out.println (test.name);}}결과:
원인 : org.springframework.beans.factory.beancreationexception : 이름 'com.alibaba.test.circle.dependentbean#1c701a27'으로 Bean 생성 오류 : 필드의 자동화 실패; 중첩 예외는 org.springframework.beans.factory.beancreationException : Autowire 필드 : private com.alibaba.test.circle.test com.alibaba.test.circle.dependentbean.test; 중첩 된 예외는 org.springframework.beans.fackory.beancurrentyincreationException : '' '테스트'로 Bean을 생성하는 오류 : 현재 생성중인 FactoryBean은 getObject에서 널 리턴 NULL
3.2 이유 분석
테스트를 인스턴스화 할 때 getBean(“test”) 이 트리거되고 현재 콩이 존재하는지 확인합니다.
존재하지 않으면 테스트 인스턴스를 작성하십시오. 생성 후, 현재 Bean 정보는 SingletonFactories 단일 피스 맵에 배치됩니다.
그런 다음 속성 부양권을 인스턴스에 주입하십시오. 속성 주입이 사용되면 getBean(“depentBean”) 사용됩니다.
부양애가 존재하지 않는 경우, 의존 비안을 인스턴스화 한 다음 싱글 톤 액토리에 넣을 것입니다.
그런 다음 자동 주사 테스트 및 getBean(“test”); 현재 (1) GetSingleton은 인스턴스화 된 테스트를 반환합니다. 테스트는 팩토리 콩이므로 return test.getObject();
MyFactoryBean의 AfterProperTiesset은 아직 호출되지 않았으므로 test.getObject() null을 반환합니다.
다음은 다음 봄 콩 생성 과정입니다.
getBean ()-> instance-> autowired-> set property-> aperpropertiesset을 작성하십시오
즉, getObject 메소드를 호출하는 것은 AfterProperTiesset 방법보다 일찍 호출되었습니다.
그런 다음 myFactoryBean을 다음과 같이 수정합시다.
공개 개체 getObject ()는 예외를 던져 {// todo 자동 생성 된 메소드 stubif (null == test) {afcProperTiesset ();} return test;} public void afterProperTiesset ()는 예외를 {if (null == test) {system.out.println ( "이름 :" + this.name); test = new test (); test.name = sexendentBean.dosomething () + this.name;}} 즉, GetObject 내에서 먼저 판단하면 test==null 그런 다음 afterProperTiesset을 호출 한 다음 test==null 이 AfterProperTiesset 내에서 테스트 인스턴스를 생성하는 경우 좋아 보이며 문제를 해결하고 싶습니다. 그러나 AfterproperTiesset은 내부적으로 종속 비안을 사용하고 현재의 depentBean=null 사용하기 때문에 실제로는 작동하지 않습니다.
3.3 그것을 해결하는 방법에 대해 생각하십시오
3.2 분석 이유는 MyFactoryBean이 처음 만들어졌고 MyFactoryBean을 만드는 과정에서 DePentbean이 만들어 졌기 때문입니다. DepentBean을 만들 때 자율적 인 MyFactoryBean의 인스턴스가 필요합니다. 그런 다음 getObject 메서드가 호출되기 전에 호출되므로 NULL이 반환됩니다.
따라서 먼저 Depentbean을 만들고 MyFactoryBean을 만들면? 다음 분석이 이루어집니다.
먼저, Depentbean은 인스턴스화되고 SingletonFactories에 추가됩니다.
DePentbean 인스턴스가 자동으로 테스트되므로 테스트 인스턴스가 먼저 생성됩니다.
테스트 인스턴스를 작성한 다음 SingletOnfactories에 가입하십시오
테스트 인스턴스는 속성에서 DepentBean 인스턴스를 주입하므로 getBean(“depentBean”);
getBean(“depentBean”)
부양애는 공장 콩이 아니기 때문에 부양비를 직접 반환합니다.
테스트 인스턴스가 DepentBean 인스턴스에 성공적으로 주입됩니다. 테스트 인스턴스 초기화 OK
DePentbean 인스턴스 자동 테스트 인스턴스 OK
이 분석에 따르면 먼저 Depentbean을 만들고 myfactorybean을 인스턴스화하는 것이 가능합니다. XML을 다음으로 수정하십시오.
<bean id = "dependentBean"> </bean> <bean id = "test"> <property name = "dependentBean"> <ref bean = "fexendentBean"/</property name = "name"value = "zlx"> </bean>
테스트 실행 결과 :
이름 : Zlx
안녕하세요 : Zlx
실제로 괜찮은 경우,이 분석에 따르면 위의 XML 구성이 조정되면 테스트가 종속 빈보다 일찍 생성 되었기 때문에 오류가 발생하며 테스트 후에도 마찬가지입니다. 또한 공장 콩이 공장 콩에 의존 할 때 선언 순서에 관계없이 필연적으로 실패 할 것이라고 상상할 수 있습니다.
3.3 생각
위의 첫 번째는 myFactoryBean에 사용되어야하는 종속 비안을 주입 한 다음 myFactoryBean을 주입하면 문제가 해결됩니다. 따라서 다른 콩에 id = "테스트"로 생성 된 객체를 사용해야한다면 어떻게이 콩을 주입해야합니까?
비슷한 방식으로 성공할까요? 모두가 생각할 수 있도록 ^^
Public Class USETEST {@autowiredPrivate Test Test;}<bean id = "usetest"> </bean> <bean id = "feenceentbean"> </bean> <bean id = "test"> <property name = "fexendentBean"> <ref bean = "dependentBean"/</property> <속성 이름 = "이름"value = "zlx"> </property> </bean
4. 요약
일반 콩이 상호 의존적 인 경우, 콩 주입 순서는 관련이 없지만 공장 콩과 일반 콩이 상호 의존적 인 경우 일반 콩을 먼저 인스턴스화해야합니다. 이것은 공장 콩의 특수성 때문입니다. 즉, getObject 방법이 있습니다.
좋아, 위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.