javascript
Spring官网阅读(一)容器及实例化
從今天開始,我們一起過一遍Spring的官網(wǎng),為Spring源碼的學(xué)習(xí)打好基礎(chǔ)。在這個(gè)過程中,不會(huì)涉及過多底層的代碼,更多是通過例子證明我們?cè)诠倬W(wǎng)得出的結(jié)論,希望自己可以堅(jiān)持下來,給自己加個(gè)油!!!
本文主要涉及到官網(wǎng)中的1.2,1.3節(jié)。
Spring容器
容器是什么?
我們先看官網(wǎng)中的一句話:
The?org.springframework.context.ApplicationContext?interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.
翻譯下來大概就是:
Spring IOC容器就是一個(gè)org.springframework.context.ApplicationContext的實(shí)例化對(duì)象
容器負(fù)責(zé)了實(shí)例化,配置以及裝配一個(gè)bean
那么我們可以說:
從代碼層次來看:Spring容器就是一個(gè)實(shí)現(xiàn)了ApplicationContext接口的對(duì)象,
從功能上來看:Spring 容器是 Spring 框架的核心,是用來管理對(duì)象的。容器將創(chuàng)建對(duì)象,把它們連接在一起,配置它們,并管理他們的整個(gè)生命周期從創(chuàng)建到銷毀。
容器如何工作?
我們直接看官網(wǎng)上的一張圖片,如下:
Spring容器通過我們提交的pojo類以及配置元數(shù)據(jù)產(chǎn)生一個(gè)充分配置的可以使用的系統(tǒng)
這里說的配置元數(shù)據(jù),實(shí)際上我們就是我們提供的XML配置文件,或者通過注解方式提供的一些配置信息
?
Spring Bean
如何實(shí)例化一個(gè)Bean?
從官網(wǎng)上來看,主要有以下三種方法
?
構(gòu)造方法
通過靜態(tài)工廠方法
通過實(shí)例工廠方法
這三種例子,官網(wǎng)都有具體的演示,這里就不再貼了,我們通過自己查閱部分源碼,來驗(yàn)證我們?cè)诠倬W(wǎng)得到的結(jié)論,然后通過debug等方式進(jìn)行驗(yàn)證。
我們?cè)購(gòu)拇a的角度進(jìn)行一波分析,這里我們直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance這個(gè)方法中,具體定位步驟不再演示了,大家可以通過形如下面這段代碼:
ClassPathXmlApplicationContext?cc?=//?這里我們通過xml配置實(shí)例化一個(gè)容器new?ClassPathXmlApplicationContext("classpath:application.xml");MyServiceImpl?luBan?=?(MyServiceImpl)?cc.getBean("myServiceImpl");直接main方法運(yùn)行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance這個(gè)方法的入口打一個(gè)斷點(diǎn),如圖:
接下來我們對(duì)這個(gè)方法進(jìn)行分析,代碼如下:
protected?BeanWrapper?createBeanInstance(String?beanName,?RootBeanDefinition?mbd,?@Nullable?Object[]?args)?{//?1.獲取這個(gè)bean的class屬性,確保beanDefinition中beanClass屬性已經(jīng)完成解析//?我們通過xml從<bean>標(biāo)簽中解析出來的class屬性在剛剛開始的時(shí)候必定是個(gè)字符串Class<?>?beanClass?=?resolveBeanClass(mbd,?beanName);//?省略異常判斷代碼.....//?2.通過beanDefinition中的supplier實(shí)例化這個(gè)beanSupplier<?>?instanceSupplier?=?mbd.getInstanceSupplier();if?(instanceSupplier?!=?null)?{return?obtainFromSupplier(instanceSupplier,?beanName);}//?3.通過FactoryMethod實(shí)例化這個(gè)beanif?(mbd.getFactoryMethodName()?!=?null)?{return?instantiateUsingFactoryMethod(beanName,?mbd,?args);}//?4.下面這段代碼都是在通過構(gòu)造函數(shù)實(shí)例化這個(gè)Bean,分兩種情況,一種是通過默認(rèn)的無參構(gòu)造,一種???????????????????是通過推斷出來的構(gòu)造函數(shù)boolean?resolved?=?false;boolean?autowireNecessary?=?false;if?(args?==?null)?{synchronized?(mbd.constructorArgumentLock)?{if?(mbd.resolvedConstructorOrFactoryMethod?!=?null)?{resolved?=?true;autowireNecessary?=?mbd.constructorArgumentsResolved;}}}if?(resolved)?{if?(autowireNecessary)?{return?autowireConstructor(beanName,?mbd,?null,?null);}else?{return?instantiateBean(beanName,?mbd);}}//?Candidate?constructors?for?autowiring?Constructor<?>[]?ctors?=?determineConstructorsFromBeanPostProcessors(beanClass,?beanName);if?(ctors?!=?null?||?mbd.getResolvedAutowireMode()?==?AUTOWIRE_CONSTRUCTOR?||mbd.hasConstructorArgumentValues()?||?!ObjectUtils.isEmpty(args))?{return?autowireConstructor(beanName,?mbd,?ctors,?args);}//?Preferred?constructors?for?default?construction?ctors?=?mbd.getPreferredConstructors();if?(ctors?!=?null)?{return?autowireConstructor(beanName,?mbd,?ctors,?null);}//?No?special?handling:?simply?use?no-arg?constructor.return?instantiateBean(beanName,?mbd);}我們主要關(guān)注進(jìn)行實(shí)例化的幾個(gè)方法:
通過BeanDefinition中的instanceSupplier直接獲取一個(gè)實(shí)例化的對(duì)象。這個(gè)instanceSupplier屬性我本身不是特別理解,在xml中的
標(biāo)簽以及注解的方式都沒有找到方式配置這個(gè)屬性。后來在org.springframework.context.support.GenericApplicationContext這個(gè)類中找到了以下兩個(gè)方法經(jīng)過斷點(diǎn)測(cè)試,發(fā)現(xiàn)這種情況下,在實(shí)例化對(duì)象時(shí)會(huì)進(jìn)入上面的supplier方法。下面是測(cè)試代碼:
public?static?void?main(String[]?args)?{//?AnnotationConfigApplicationContext是GenericApplicationContext的一個(gè)子類AnnotationConfigApplicationContext?ac?=?new?AnnotationConfigApplicationContext();ac.registerBean("service",?Service.class,Service::new);ac.refresh();System.out.println(ac.getBean("service"));}可以發(fā)現(xiàn)進(jìn)入了這個(gè)方法進(jìn)行實(shí)例化
這個(gè)方法一般不常用,平常我們也使用不到,就不做過多探究,筆者認(rèn)為,這應(yīng)該是Spring提供的一種方便外部擴(kuò)展的手段,讓開發(fā)者能夠更加靈活的實(shí)例化一個(gè)bean。
?
接下來我們通過不同的創(chuàng)建bean的手段,來分別驗(yàn)證對(duì)象的實(shí)例化方法
-
通過@compent,@Service等注解的方式
測(cè)試代碼:
public?class?Main?{public?static?void?main(String[]?args)?{//?通過配置類掃描AnnotationConfigApplicationContext?ac?=?new?AnnotationConfigApplicationContext(Config.class);System.out.println(ac.getBean(Service.class));} }@Component public?class?Service?{}觀察debug:
可以發(fā)現(xiàn),代碼執(zhí)行到最后一行,同時(shí)我們看代碼上面的注釋可以知道,當(dāng)沒有進(jìn)行特殊的處理的時(shí)候,默認(rèn)會(huì)使用無參構(gòu)造函數(shù)進(jìn)行對(duì)象的實(shí)例化
- 通過普通XML的方式(同@compent注解,這里就不贅訴了)
- 通過@Configuration注解的方式
測(cè)試代碼:
?
public?class?Main?{public?static?void?main(String[]?args)?{//?通過配置類掃描AnnotationConfigApplicationContext?ac?=?new?AnnotationConfigApplicationContext(Config.class);//?這里將測(cè)試對(duì)象換為config即可,同時(shí)記得將條件斷點(diǎn)更改為beanName.equlas("config")System.out.println(ac.getBean(config.class));} }同樣,斷點(diǎn)也進(jìn)入最后一行
- 通過@Bean的方式
?
測(cè)試代碼:
@Configuration @ComponentScan("com.dmz.official") public?class?Config?{@Beanpublic?Service?service(){return?new?Service();} }public?class?Main?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?ac?=new?AnnotationConfigApplicationContext(Config.class);System.out.println(ac.getBean("service"));}}斷點(diǎn)結(jié)果:
可以發(fā)現(xiàn),通過@Bean方法創(chuàng)建對(duì)象時(shí),Spring底層是通過factoryMethod的方法進(jìn)行實(shí)例化對(duì)象的。Spring會(huì)在我們需要實(shí)例化的這個(gè)對(duì)象對(duì)應(yīng)的BeanDefinition中記錄factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同時(shí)會(huì)記錄這個(gè)factoryBean中創(chuàng)建對(duì)象的factoryMethodName是什么,最后通過factoryBeanName獲取一個(gè)Bean然后反射調(diào)用factoryMethod實(shí)例化一個(gè)對(duì)象。
?
這里我們需要注意幾個(gè)概念:
這里所說的通過靜態(tài)工廠方式通過factoryBeanName獲取一個(gè)Bean,注意,這個(gè)Bean,不是一個(gè)FactoryBean。也就是說不是一個(gè)實(shí)現(xiàn)了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我們?cè)诤竺娴奈恼聲?huì)認(rèn)真分析
提到了一個(gè)概念BeanDefinition,它就是Spring對(duì)自己所管理的Bean的一個(gè)抽象。不懂可以暫且跳過,后面有文章會(huì)講到。
-
通過靜態(tài)工廠方法的方式
測(cè)試代碼:
public?static?void?main(String[]?args)?{ClassPathXmlApplicationContext?cc?=new?ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("service"));} <?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="myServiceImpl"?class="com.dmz.official.service.Service"/>--><!--?the?factory?bean,?which?contains?a?method?called?get()?--><bean?id="myFactoryBean"?class="com.dmz.official.service.MyFactoryBean"><!--?inject?any?dependencies?required?by?this?locator?bean?--></bean><!--?測(cè)試實(shí)例工廠方法創(chuàng)建對(duì)象--><bean?id="clientService"factory-bean="myFactoryBean"factory-method="get"/><!--測(cè)試靜態(tài)工廠方法創(chuàng)建對(duì)象--><bean?id="service"class="com.dmz.official.service.MyFactoryBean"factory-method="staticGet"/> </beans>斷點(diǎn)如下:
?
可以發(fā)現(xiàn),這種情況也進(jìn)入了instantiateUsingFactoryMethod方法中。通過靜態(tài)工廠方法這種方式特殊之處在于,包含這個(gè)靜態(tài)方法的類,不需要實(shí)例化,不需要被Spring管理。Spring的調(diào)用邏輯大概是:
通過<bean>標(biāo)簽中的class屬性得到一個(gè)Class對(duì)象
通過Class對(duì)象獲取到對(duì)應(yīng)的方法名稱的Method對(duì)象
最后反射調(diào)用Method.invoke(null,args)
因?yàn)槭庆o態(tài)方法,方法在執(zhí)行時(shí),不需要一個(gè)對(duì)象。
-
通過實(shí)例工廠方法的方式
測(cè)試代碼(配置文件不變):
public?static?void?main(String[]?args)?{ClassPathXmlApplicationContext?cc?=new?ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("clientService"));}斷點(diǎn)如下:
?
還是執(zhí)行的這個(gè)方法。這個(gè)方法的執(zhí)行過程我斷點(diǎn)跟蹤了以后,發(fā)現(xiàn)跟@Bean方式執(zhí)行的流程是一樣的。這里也不再贅述了。
到這里,這段代碼我們算結(jié)合官網(wǎng)大致過了一遍。其實(shí)還遺留了以下幾個(gè)問題:
Spring是如何推斷構(gòu)造函數(shù)的?我們?cè)谏厦骝?yàn)證的都是無參的構(gòu)造函數(shù),并且只提供了一個(gè)構(gòu)造函數(shù)
Spring是如何推斷方法的?不管是靜態(tài)工廠方法,還是實(shí)例工廠方法的方式,我們都只在類中提供了一個(gè)跟配置匹配的方法名,假設(shè)我們對(duì)方法進(jìn)行了重載呢?
要說清楚這兩個(gè)問題需要比較深入的研究代碼,同時(shí)進(jìn)行測(cè)試。我們?cè)诠倬W(wǎng)學(xué)習(xí)過程中,暫時(shí)不去強(qiáng)求這類問題。這里提出來是為了在源碼學(xué)習(xí)過程中,我們可以帶一定目的性去閱讀。
實(shí)例化總結(jié):
對(duì)象實(shí)例化,只是得到一個(gè)對(duì)象,還不是一個(gè)完全的Spring中的Bean,我們實(shí)例化后的這個(gè)對(duì)象還沒有完成依賴注入,沒有走完一系列的聲明周期,這里需要大家注意
Spring官網(wǎng)上指明了,在Spring中實(shí)例化一個(gè)對(duì)象有三種方式:
-
構(gòu)造函數(shù)
-
實(shí)例工廠方法
-
靜態(tài)工廠方法
我自己總結(jié)如下結(jié)論:
Spring通過解析我們的配置元數(shù)據(jù),以及我們提供的類對(duì)象得到一個(gè)Beanfinition對(duì)象。通過這個(gè)對(duì)象可以實(shí)例化出一個(gè)java bean對(duì)象。主要流程如圖:
?
這篇文章到這里就結(jié)束了,主要學(xué)習(xí)了Spring官網(wǎng)中的1.2,1.3兩小節(jié)。下篇文章,我們開始學(xué)習(xí)1.4中的知識(shí)。主要涉及到依賴注入的一些內(nèi)容,也是我們Spring中非常重要的一塊內(nèi)容哦!下篇文章再見!
總結(jié)
以上是生活随笔為你收集整理的Spring官网阅读(一)容器及实例化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring 是解析配置类过程详解
- 下一篇: 知乎高赞:985计算机视觉毕业后找不到工