學習目標
1)搞清楚構造參數依賴注入的過程及類
2)搞清楚註解方式的屬性依賴注入在哪裏完成的。
學習思路
1)思考咱們手寫時是如何作的
2)讀 spring 源碼對比看它的實現
3)Spring 源碼解讀java
org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(String, RootBeanDefinition, Constructor<?>[], Object[])spring
->
org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(String, RootBeanDefinition, BeanWrapper, ConstructorArgumentValues, ConstructorArgumentValues)緩存
、、app
->
org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(Object, Object)源碼分析
解析構造參數源代碼的使用示例:post
<bean id="combatService" factory-bean="loveServiceFactory" factory-method="getCombatServiceFromMemberFactoryMethod" > <constructor-arg type="int" value="120" /> <constructor-arg ref="beanA"></constructor-arg> <constructor-arg><bean id="ssss" class="cccc"></bean></constructor-arg> <constructor-arg><bean class="cccc"></bean></constructor-arg> <constructor-arg><map></map></constructor-arg> <constructor-arg><list></list></constructor-arg> </bean>
拓展:初始化前初始化後處理學習
實現BeanPostProcessor,而後在裏面的初始化前和初始化後的方法裏面打斷點看調用棧就能夠找到初始化前和初始化後在哪裏處理的了測試
實現BeanPostProcessor:this
package com.study.leesmall.spring.ext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("------MyBeanPostProcessor.postProcessBeforeInitialization for " + beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("------MyBeanPostProcessor.postProcessAfterInitialization for " + beanName);
return bean;
}
}
在初始化前方法MyBeanPostProcessor.postProcessBeforeInitialization和MyBeanPostProcessor.postProcessAfterInitialization裏面打斷點拿到調用棧:spa
初始化前調用棧:
具體的初始化前處理:
初始化後調用棧:
具體的初始化後處理:
寫個測試類裏面含有get和set方法,而後在set方法裏面打個斷點拿到調用棧去分析
測試類:
import org.springframework.stereotype.Component; @Component public class Bean3 { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
在xml裏面的配置:
<bean id="bean3" class="com.study.leesmall.spring.sample.di.Bean3" scope="prototype" > <property name="value" value="10"></property> </bean>
測試入口:
package com.study.leesmall.spring.sample.di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class DiMain { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext( "classpath:com/study/leesmall/spring/sample/di/application.xml"); Bean3 b3 = context.getBean(Bean3.class); } }
在set方法裏面打個斷點拿到調用棧:
屬性依賴值處理源碼分析:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(String, RootBeanDefinition, BeanWrapper)
構成參數是不容許循環依賴的,屬性是容許循環依賴的
Bean1:
package com.study.leesmall.spring.sample.di; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; public class Bean1 { @Autowired private Bean2 b2; public void do1() { System.out.println("------------------do1"); } public Bean2 getB2() { return b2; } public void setB2(Bean2 b2) { this.b2 = b2; } }
Bean2:
package com.study.leesmall.spring.sample.di; import org.springframework.stereotype.Component; public class Bean2 { @Autowired private Bean1 b1; public void do2() { b1.do1(); } public Bean1 getB1() { return b1; } public void setB1(Bean1 b1) { this.b1 = b1; } }
/spring-source-study/src/main/java/com/study/leesmall/spring/sample/di/application.xml配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bean1" class="com.study.leesmall.spring.sample.di.Bean1" > <property name="b2" ref="bean2"></property> </bean> <bean id="bean2" class="com.study.leesmall.spring.sample.di.Bean2" > <property name="b1" ref="bean1"></property> </bean> </beans>
測試類DiMain:
package com.study.leesmall.spring.sample.di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class DiMain { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext( "classpath:com/study/leesmall/spring/sample/di/application.xml"); Bean2 b2 = context.getBean(Bean2.class); b2.do2(); } }
a)Bean1和Bean2都是單例bean。
循環依賴成功
b)Bean1和Bean2一個是單例的,一個是原型的。
循環依賴成功
c)Bean1和Bean2都是原型的
循環依賴不成功
3)思考:爲何兩個是原型時不能循環依賴,而單例時能夠?
一個Bean的屬性依賴另外一個bean實例,注入的過程是否就是從BeanFactory中getBean(),再賦值給屬性?
是,首先從Bean工廠裏面獲取依賴的bean,沒有就建立
那爲何單例能夠,原型不能夠?
依據上面的邏輯,那就單例時能夠getBean()得到依賴的bean實例,原型時不能,爲何?
再來回想一下單例bean和原型bean在BeanFactory中的區別:
單例Bean是緩存在BeanFactory中的,而原型Bean是不緩存的。
緩存和不緩存對於循環依賴的處理有什麼不一樣呢?
思考一下建立Bean實例的過程:
先Bean1,建立Bean1的實例——>而後對其屬性進行依賴注入處理——>依賴Bean2,從BeanFactorygetBean("bean2")——>建立Bean2的實例——>對Bean2實例的屬性進行依賴注入處理——>依賴Bean1,從BeanFactory中獲取Bean1的實例
緩存的就能夠經過beanFactory的getBean()得到前一個Bean的實例。而若是不緩存的,則bean2實例依賴注入Bean1時,從BeanFactorygetBean()就會又建立一個Bean1的實例,如此會無限循環依賴建立下去。
再仔細想一下,對於單例bean的緩存有時機的要求嗎?
有,必定要在進行屬性依賴注入處理前緩存(暴露)到BeanFactory中。
在AbstractAutowireCapableBeanFactory.doCreateBean()方法中
// 爲循環引用依賴提早緩存單例 bean // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // 是否提前暴露單例 Bean 實例 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace( "Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
代碼在AbstractAutowireCapableBeanFactory.populateBean()中。
Xml配置方式的處理邏輯在方法最後的applyPropertyValues(beanName,mbd,bw,pvs);方法中。
註解的方式則在之上的InstantiationAwareBeanPostProcessor執行中:
if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } }
BeanF:
package com.study.leesmall.spring.sample.config; import org.springframework.beans.factory.annotation.Autowired; public class BeanF { @Autowired private BeanE be; public void do1() { System.out.println("----------" + this + " do1"); this.be.doSomething(); } }
BeanE:
package com.study.leesmall.spring.sample.config; public class BeanE { public void doSomething() { System.out.println("-----" + this + " doSomething "); } }
xml配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="beanE" class="com.study.leesmall.spring.sample.config.BeanE" /> <bean id="beanF" class="com.study.leesmall.spring.sample.config.BeanF" ></bean> <context:annotation-config/> <context:component-scan base-package="com.study.leesmall.spring.sample.config" ></context:component-scan> </beans>
測試類:
XMLConfigMain
package com.study.leesmall.spring.sample.config; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class XMLConfigMain { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext( "classpath:com/study/leesmall/spring/sample/config/application.xml"); BeanF bf = context.getBean(BeanF.class); bf.do1(); } }
測試類運行結果:
----------com.study.leesmall.spring.sample.config.BeanF@19d37183 do1
-----com.study.leesmall.spring.sample.config.BeanE@1a0dcaa doSomething
BeanG
package com.study.leesmall.spring.sample.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ImportResource; import org.springframework.stereotype.Component; @Component @ImportResource("classpath:com/study/leesmall/spring/sample/config/application.xml") public class BeanG { @Autowired private BeanF beanf; public void dog() { System.out.println("----------------------------------------"); this.beanf.do1(); } }
在xml裏面開啓註解掃描:
<context:annotation-config/> <context:component-scan base-package="com.study.leesmall.spring.sample.config" ></context:component-scan>
測試類:
AnnotationMain
package com.study.leesmall.spring.sample.config; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AnnotationMain { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext("com.study.leesmall.spring.sample.config"); BeanG bg = context.getBean(BeanG.class); bg.dog(); } }
測試結果:
----------------------------------------
----------com.study.leesmall.spring.sample.config.BeanF@6f204a1a do1
-----com.study.leesmall.spring.sample.config.BeanE@2de56eb2 doSomething
BeanH:
package com.study.leesmall.spring.sample.config; import org.springframework.beans.factory.annotation.Autowired; public class BeanH { @Autowired private BeanE be; public void doH() { System.out.println("-----------" + this + " doH"); be.doSomething(); } }
測試類:
JavaBasedMain
package com.study.leesmall.spring.sample.config; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.study.leesmall.spring.sample.config") public class JavaBasedMain { @Bean public BeanH getBeanH() { return new BeanH(); } public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(JavaBasedMain.class); BeanH bh = context.getBean(BeanH.class); bh.doH(); } }
測試結果:
-----------com.study.leesmall.spring.sample.config.BeanH@475e586c doH
-----com.study.leesmall.spring.sample.config.BeanE@657c8ad9 doSomething
問題:
一、 xml 方式中怎麼開啓註解支持?
在application.xml裏面加入以下配置:
<context:annotation-config/> <context:component-scan base-package="com.study.leesmall.spring.sample.config" ></context:component-scan>
二、xml方式中開啓註解支持,是如何實現的?該怎麼去看?你會怎麼實現?
入口在Spring的E:\repository\org\springframework\spring-context\5.1.3.RELEASE\spring-context-5.1.3.RELEASE.jar /META-INF/spring.handlers裏面
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
ContextNamespaceHandler就是入口:
類org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser就是用來解析下面的配置的:
<context:annotation-config/>
註冊註解配置處理器的方法org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)
Autowired註解的處理屬性注入的方法:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(PropertyValues, Object, String)
org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement.inject(Object, String, PropertyValues):
三、Xml中的<context:component-scan>是如何實現的?四、註解方式能夠嵌入xml嗎?五、Javabase方式各註解的解析發生在哪