日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring官网阅读(一)容器及实例化

發布時間:2025/3/21 javascript 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring官网阅读(一)容器及实例化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

從今天開始,我們一起過一遍Spring的官網,為Spring源碼的學習打好基礎。在這個過程中,不會涉及過多底層的代碼,更多是通過例子證明我們在官網得出的結論,希望自己可以堅持下來,給自己加個油!!!

本文主要涉及到官網中的1.2,1.3節。

Spring容器

容器是什么?

我們先看官網中的一句話:

The?org.springframework.context.ApplicationContext?interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.

翻譯下來大概就是:

  • Spring IOC容器就是一個org.springframework.context.ApplicationContext的實例化對象

  • 容器負責了實例化,配置以及裝配一個bean

  • 那么我們可以說:

    從代碼層次來看:Spring容器就是一個實現了ApplicationContext接口的對象

    從功能上來看:Spring 容器是 Spring 框架的核心,是用來管理對象的。容器將創建對象,把它們連接在一起,配置它們,并管理他們的整個生命周期從創建到銷毀。

    容器如何工作?

    我們直接看官網上的一張圖片,如下:

    Spring容器通過我們提交的pojo類以及配置元數據產生一個充分配置的可以使用的系統

    這里說的配置元數據,實際上我們就是我們提供的XML配置文件,或者通過注解方式提供的一些配置信息

    ?

    Spring Bean

    如何實例化一個Bean?

    從官網上來看,主要有以下三種方法

    ?

  • 構造方法

  • 通過靜態工廠方法

  • 通過實例工廠方法

  • 這三種例子,官網都有具體的演示,這里就不再貼了,我們通過自己查閱部分源碼,來驗證我們在官網得到的結論,然后通過debug等方式進行驗證。

    我們再從代碼的角度進行一波分析,這里我們直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance這個方法中,具體定位步驟不再演示了,大家可以通過形如下面這段代碼:

    ClassPathXmlApplicationContext?cc?=//?這里我們通過xml配置實例化一個容器new?ClassPathXmlApplicationContext("classpath:application.xml");MyServiceImpl?luBan?=?(MyServiceImpl)?cc.getBean("myServiceImpl");

    直接main方法運行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance這個方法的入口打一個斷點,如圖:

    接下來我們對這個方法進行分析,代碼如下:

    protected?BeanWrapper?createBeanInstance(String?beanName,?RootBeanDefinition?mbd,?@Nullable?Object[]?args)?{//?1.獲取這個bean的class屬性,確保beanDefinition中beanClass屬性已經完成解析//?我們通過xml從<bean>標簽中解析出來的class屬性在剛剛開始的時候必定是個字符串Class<?>?beanClass?=?resolveBeanClass(mbd,?beanName);//?省略異常判斷代碼.....//?2.通過beanDefinition中的supplier實例化這個beanSupplier<?>?instanceSupplier?=?mbd.getInstanceSupplier();if?(instanceSupplier?!=?null)?{return?obtainFromSupplier(instanceSupplier,?beanName);}//?3.通過FactoryMethod實例化這個beanif?(mbd.getFactoryMethodName()?!=?null)?{return?instantiateUsingFactoryMethod(beanName,?mbd,?args);}//?4.下面這段代碼都是在通過構造函數實例化這個Bean,分兩種情況,一種是通過默認的無參構造,一種???????????????????是通過推斷出來的構造函數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);}

    我們主要關注進行實例化的幾個方法:

  • 通過BeanDefinition中的instanceSupplier直接獲取一個實例化的對象。這個instanceSupplier屬性我本身不是特別理解,在xml中的

    標簽以及注解的方式都沒有找到方式配置這個屬性。后來在org.springframework.context.support.GenericApplicationContext這個類中找到了以下兩個方法
  • 經過斷點測試,發現這種情況下,在實例化對象時會進入上面的supplier方法。下面是測試代碼:

    public?static?void?main(String[]?args)?{//?AnnotationConfigApplicationContext是GenericApplicationContext的一個子類AnnotationConfigApplicationContext?ac?=?new?AnnotationConfigApplicationContext();ac.registerBean("service",?Service.class,Service::new);ac.refresh();System.out.println(ac.getBean("service"));}

    可以發現進入了這個方法進行實例化

    這個方法一般不常用,平常我們也使用不到,就不做過多探究,筆者認為,這應該是Spring提供的一種方便外部擴展的手段,讓開發者能夠更加靈活的實例化一個bean。

    ?

  • 接下來我們通過不同的創建bean的手段,來分別驗證對象的實例化方法

    • 通過@compent,@Service等注解的方式

    測試代碼:

    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:

    可以發現,代碼執行到最后一行,同時我們看代碼上面的注釋可以知道,當沒有進行特殊的處理的時候,默認會使用無參構造函數進行對象的實例化

    • 通過普通XML的方式(同@compent注解,這里就不贅訴了)
    • 通過@Configuration注解的方式

    測試代碼:

    ?

    public?class?Main?{public?static?void?main(String[]?args)?{//?通過配置類掃描AnnotationConfigApplicationContext?ac?=?new?AnnotationConfigApplicationContext(Config.class);//?這里將測試對象換為config即可,同時記得將條件斷點更改為beanName.equlas("config")System.out.println(ac.getBean(config.class));} }

    同樣,斷點也進入最后一行

    • 通過@Bean的方式

    ?

    測試代碼:

    @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"));}}

    斷點結果:

    可以發現,通過@Bean方法創建對象時,Spring底層是通過factoryMethod的方法進行實例化對象的。Spring會在我們需要實例化的這個對象對應的BeanDefinition中記錄factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同時會記錄這個factoryBean中創建對象的factoryMethodName是什么,最后通過factoryBeanName獲取一個Bean然后反射調用factoryMethod實例化一個對象。

    ?

    這里我們需要注意幾個概念:

  • 這里所說的通過靜態工廠方式通過factoryBeanName獲取一個Bean,注意,這個Bean,不是一個FactoryBean。也就是說不是一個實現了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我們在后面的文章會認真分析

  • 提到了一個概念BeanDefinition,它就是Spring對自己所管理的Bean的一個抽象。不懂可以暫且跳過,后面有文章會講到。

    • 通過靜態工廠方法的方式

    測試代碼:

    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><!--?測試實例工廠方法創建對象--><bean?id="clientService"factory-bean="myFactoryBean"factory-method="get"/><!--測試靜態工廠方法創建對象--><bean?id="service"class="com.dmz.official.service.MyFactoryBean"factory-method="staticGet"/> </beans>

    斷點如下:

    ?

    可以發現,這種情況也進入了instantiateUsingFactoryMethod方法中。通過靜態工廠方法這種方式特殊之處在于,包含這個靜態方法的類,不需要實例化,不需要被Spring管理。Spring的調用邏輯大概是:

  • 通過<bean>標簽中的class屬性得到一個Class對象

  • 通過Class對象獲取到對應的方法名稱的Method對象

  • 最后反射調用Method.invoke(null,args)

  • 因為是靜態方法,方法在執行時,不需要一個對象。

    • 通過實例工廠方法的方式

    測試代碼(配置文件不變):

    public?static?void?main(String[]?args)?{ClassPathXmlApplicationContext?cc?=new?ClassPathXmlApplicationContext("application.xml");System.out.println(cc.getBean("clientService"));}

    斷點如下:

    ?

    還是執行的這個方法。這個方法的執行過程我斷點跟蹤了以后,發現跟@Bean方式執行的流程是一樣的。這里也不再贅述了。

    到這里,這段代碼我們算結合官網大致過了一遍。其實還遺留了以下幾個問題:

  • Spring是如何推斷構造函數的?我們在上面驗證的都是無參的構造函數,并且只提供了一個構造函數

  • Spring是如何推斷方法的?不管是靜態工廠方法,還是實例工廠方法的方式,我們都只在類中提供了一個跟配置匹配的方法名,假設我們對方法進行了重載呢?

  • 要說清楚這兩個問題需要比較深入的研究代碼,同時進行測試。我們在官網學習過程中,暫時不去強求這類問題。這里提出來是為了在源碼學習過程中,我們可以帶一定目的性去閱讀。

    實例化總結:

  • 對象實例化,只是得到一個對象,還不是一個完全的Spring中的Bean,我們實例化后的這個對象還沒有完成依賴注入,沒有走完一系列的聲明周期,這里需要大家注意

  • Spring官網上指明了,在Spring中實例化一個對象有三種方式:

    • 構造函數

    • 實例工廠方法

    • 靜態工廠方法

  • 我自己總結如下結論:

    Spring通過解析我們的配置元數據,以及我們提供的類對象得到一個Beanfinition對象。通過這個對象可以實例化出一個java bean對象。主要流程如圖:

  • ?

    這篇文章到這里就結束了,主要學習了Spring官網中的1.2,1.3兩小節。下篇文章,我們開始學習1.4中的知識。主要涉及到依賴注入的一些內容,也是我們Spring中非常重要的一塊內容哦!下篇文章再見!

    總結

    以上是生活随笔為你收集整理的Spring官网阅读(一)容器及实例化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。