javascript
spring图片转视频_一直在用的 Spring,你知道它的加载原理吗?
? 幫助萬(wàn)千Java學(xué)習(xí)者持續(xù)成長(zhǎng)
關(guān)注作者|擁抱心中的夢(mèng)想
juejin.im/post/5ab30714f265da237b21fbcc
B 站搜索:楠哥教你學(xué)Java
獲取更多優(yōu)質(zhì)視頻教程
一、前言
作為一個(gè)經(jīng)常使用 Spring 的后端程序員,小編很早就想徹底弄懂整個(gè) Spring 框架了!但它整體是非常大的,所有繼承圖非常復(fù)雜,加上小編修行尚淺,顯得力不從心。不過(guò),男兒在世當(dāng)立志,今天就先從 Spring IOC 容器的初始化開(kāi)始說(shuō)起,即使完成不了對(duì)整個(gè) Spring 框架的完全掌握,也不丟人,因?yàn)樾【巹?dòng)手了,穩(wěn)住,咱能贏!
下面說(shuō)一些閱讀前的建議:
1、閱讀源碼分析是非常無(wú)聊的,但既然你進(jìn)來(lái)了,肯定也是對(duì)這個(gè)東西進(jìn)行了解,也希望這篇總結(jié)能對(duì)你有所啟發(fā)。
2、前方高能,文章可能會(huì)非常的長(zhǎng),圖文并茂。
3、閱讀前建議你對(duì)相關(guān)設(shè)計(jì)模式、軟件設(shè)計(jì) 6 大原則有所了解,小編會(huì)在行文中進(jìn)行穿插。
4、如果你發(fā)現(xiàn)文章觀點(diǎn)有所錯(cuò)誤或者與你見(jiàn)解有差異,歡迎指出和交流!
5、建議你邊看文章的時(shí)候可以邊在 IDE 中進(jìn)行調(diào)試跟蹤
6、文章所有 UML 圖利用 idea 自動(dòng)生成,具體生成方法為:選中一個(gè)類名,先ctrl+shift+alt+U,再ctrl+alt+B,然后回車即可
二、文章將圍繞什么來(lái)進(jìn)行展開(kāi)?
不多,就一行代碼,如下圖:
這句是 Spring 初始化的代碼,雖然只有一句代碼,但內(nèi)容賊多!
三、Spring 容器 IOC 有哪些東西組成?
這樣子,小編先理清下思路,一步一步來(lái):
1、上面那句代碼有個(gè)文件叫applicationContext.xml, 這是個(gè)資源文件,由于我們的bean都在里邊進(jìn)行配置定義,那 Spring 總得對(duì)這個(gè)文件進(jìn)行讀取并解析吧!所以 Spring 中有個(gè)模塊叫Resource模塊,顧名思義,就是資源嘛!用于對(duì)所有資源xml、txt、property等文件資源的抽象。
下面先貼一張小編生成的類圖(圖片有點(diǎn)大,不知道會(huì)不會(huì)不清晰,如果不清晰可以按照上面說(shuō)的idea生成方法去生成即可):
可以看到Resource是整個(gè)體系的根接口,點(diǎn)進(jìn)源碼可以看到它定義了許多的策略方法,因?yàn)樗怯昧瞬呗阅J竭@種設(shè)計(jì)模式,運(yùn)用的好處就是策略接口/類定義了同一的策略,不同的子類有不同的具體策略實(shí)現(xiàn),客戶端調(diào)用時(shí)傳入一個(gè)具體的實(shí)現(xiàn)對(duì)象比如UrlResource或者FileSystemResource給策略接口/類Resource即可!
所有策略如下:
2、上面講了 Spring 框架對(duì)各種資源的抽象采用了策略模式,那么問(wèn)題來(lái)了,現(xiàn)在表示資源的東西有了,那么是怎么把該資源加載進(jìn)來(lái)呢?于是就有了下面的ResourceLoader組件,該組件負(fù)責(zé)對(duì) Spring 資源的加載,資源指的是xml、properties等文件資源,返回一個(gè)對(duì)應(yīng)類型的Resource對(duì)象。。UML 圖如下:
從上面的 UML 圖可以看出,ResourceLoader組件其實(shí)跟Resource組件差不多,都是一個(gè)根接口,對(duì)應(yīng)有不同的子類實(shí)現(xiàn),比如加載來(lái)自文件系統(tǒng)的資源,則可以使用FileSystemResourceLoader, 加載來(lái)自ServletContext上下文的資源,則可以使用ServletContextResourceLoader。還有最重要的一點(diǎn)。
從上圖看出,ApplicationContext,AbstractApplication是實(shí)現(xiàn)了ResourceLoader的,這說(shuō)明什么呢?說(shuō)明我們的應(yīng)用上下文ApplicationContext擁有加載資源的能力,這也說(shuō)明了為什么可以通過(guò)傳入一個(gè)String resource path給ClassPathXmlApplicationContext("applicationContext.xml")就能獲得 xml 文件資源的原因了!清晰了嗎?nice!
3、上面兩點(diǎn)講到了,好!既然我們擁有了加載器ResourceLoader,也擁有了對(duì)資源的描述Resource, 但是我們?cè)?xml 文件中聲明的標(biāo)簽在 Spring 又是怎么表示的呢?
注意這里只是說(shuō)對(duì)bean的定義,而不是說(shuō)如何將轉(zhuǎn)換為bean對(duì)象。我想應(yīng)該不難理解吧!就像你想表示一個(gè)學(xué)生Student,那么你在程序中肯定要聲明一個(gè)類Student吧!
至于學(xué)生數(shù)據(jù)是從excel導(dǎo)入,或者程序運(yùn)行時(shí)new出來(lái),或者從xml中加載進(jìn)來(lái)這些都不重要,重要的是你要有一個(gè)將現(xiàn)實(shí)中的實(shí)體表示為程序中的對(duì)象的東西,所以也需要在 Spring 中做一個(gè)定義!于是就引入一個(gè)叫BeanDefinition的組件,UML 圖如下:
下面講解下 UML 圖:
首先配置文件中的標(biāo)簽跟我們的BeanDefinition是一一對(duì)應(yīng)的,元素標(biāo)簽擁有class、scope、lazy-init等配置屬性,BeanDefinition則提供了相應(yīng)的beanClass、scope、lazyInit屬性。
4、有了加載器ResourceLoader,也擁有了對(duì)資源的描述Resource,也有了對(duì)bean的定義,我們不禁要問(wèn),我們的Resource資源是怎么轉(zhuǎn)成我們的BeanDefinition的呢? 因此就引入了BeanDefinitionReader組件, Reader 嘛!就是一種讀取機(jī)制,UML 圖如下:
從上面可以看出,Spring 對(duì) reader 進(jìn)行了抽象,具體的功能交給其子類去實(shí)現(xiàn),不同的實(shí)現(xiàn)對(duì)應(yīng)不同的類,如PropertiedBeanDefinitionReader,XmlBeanDefinitionReader對(duì)應(yīng)從 Property 和 xml 的 Resource 解析成BeanDefinition。
5、好了!基本上所有組件都快齊全了!對(duì)了,還有一個(gè)組件,你有了BeanDefinition后,你還必須將它們注冊(cè)到工廠中去,所以當(dāng)你使用getBean()方法時(shí)工廠才知道返回什么給你。
還有一個(gè)問(wèn)題,既然要保存注冊(cè)這些bean, 那肯定要有個(gè)數(shù)據(jù)結(jié)構(gòu)充當(dāng)容器吧!沒(méi)錯(cuò),就是一個(gè)Map, 下面貼出BeanDefinitionRegistry的一個(gè)實(shí)現(xiàn),叫SimpleBeanDefinitionRegistry的源碼圖:
BeanDefinitionRegistry的 UML 圖如下:
從圖中可以看出,BeanDefinitionRegistry有三個(gè)默認(rèn)實(shí)現(xiàn),分別是SimpleBeanDefinitionRegistry,DefaultListableBeanFactory,GenericApplicationContext, 其中SimpleBeanDefinitionRegistry,DefaultListableBeanFactory都持有一個(gè) Map。
也就是說(shuō)這兩個(gè)實(shí)現(xiàn)類把保存了 bean。而GenericApplicationContext則持有一個(gè)DefaultListableBeanFactory對(duì)象引用用于獲取里邊對(duì)應(yīng)的 Map。在DefaultListableBeanFactory中
在GenericApplicationContext中
6、前面說(shuō)的 5 個(gè)點(diǎn)基本上可以看出ApplicationContext上下文基本直接或間接貫穿所有的部分,因此我們一般稱之為容器,除此之外,ApplicationContext還擁有除了bean容器這種角色外,還包括了獲取整個(gè)程序運(yùn)行的環(huán)境參數(shù)等信息(比如 JDK 版本,jre 等),其實(shí)這部分 Spring 也做了對(duì)應(yīng)的封裝,稱之為Enviroment, 下面就跟著小編的 eclipse, 一起 debug 下容器的初始化工程吧!
四、實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)
學(xué)生類Student.java如下:
package com.wokao666;public class Student { private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public Student() { super(); } @Override public String toString() { return "Student [id=" + id + ", ]"; }}在application.xml中進(jìn)行配置,兩個(gè)bean:
<bean id="stu1" class="com.wokao666.Student"> <property >property> <property >property> <property >property> bean> <bean id="stu2" class="com.wokao666.Student"> <property >property> <property >property> <property >property> bean>好了,接下來(lái)給最開(kāi)頭那段代碼打個(gè)斷點(diǎn) (Breakpoint):
第一步:急切地加載ContextClosedEvent類,以避免在WebLogic 8.1中的應(yīng)用程序關(guān)閉時(shí)出現(xiàn)奇怪的類加載器問(wèn)題。
這一步無(wú)需太過(guò)在意!
第二步:既然是new ClassPathXmlApplicationContext()?那么就調(diào)用構(gòu)造器嘛!
第三步:
第四步:
好,我們跟著第三步中的super(parent),再結(jié)合上面第三節(jié)的第 6 小點(diǎn) UML 圖一步一步跟蹤,然后我們來(lái)到AbstractApplicationContext的這個(gè)方法:
那么里邊的resourcePatternResolver的類型是什么呢?屬于第三節(jié)說(shuō)的 6 大步驟的哪個(gè)部分呢?通過(guò)跟蹤可以看到它的類型是ResourcePatternResolver類型的,而ResourcePatternResolver又是繼承了ResourceLoader接口,因此屬于加載資源模塊,如果還不清晰,咱們?cè)倏纯碦esourcePatternResolver的源碼即可,如下圖:
對(duì)吧!不僅繼承ResourceLoader接口,而且只定義一個(gè)getResources()方法用于返回Resource[]資源集合。再者,這個(gè)接口還使用了策略模式,其具體的實(shí)現(xiàn)都在實(shí)現(xiàn)類當(dāng)中,好吧!來(lái)看看 UML 圖就知道了!
PathMatchingResourcePatternResolver這個(gè)實(shí)現(xiàn)類呢!它就是用來(lái)解釋不同路徑資源的,比如你傳入的資源路徑有可能是一個(gè)常規(guī)的url, 又或者有可能是以classpath*前綴,都交給它處理。
ServletContextResourcePatternResolver這個(gè)實(shí)現(xiàn)類顧名思義就是用來(lái)加載Servlet上下文的,通常用在 web 中。
第五步:
接著第四步的方法,我們?cè)谖催M(jìn)入第四步的方法時(shí),此時(shí)會(huì)對(duì)AbstractApplicationContext進(jìn)行實(shí)例化,此時(shí)this對(duì)象的某些屬性被初始化了(如日志對(duì)象),如下圖:
接著進(jìn)入getResourcePatternResolver()方法:
第四步說(shuō)了,PathMatchingResourcePatternResolver用來(lái)處理不同的資源路徑的,怎么處理,我們先進(jìn)去看看!
如果找到,此時(shí)控制臺(tái)會(huì)打印找到用于OSGi包URL解析的Equinox FileLocator日志。沒(méi)打印很明顯找不到!
運(yùn)行完成返回setParent()方法。
第六步:
如果父代是非null,,則該父代與當(dāng)前this應(yīng)用上下文環(huán)境合并。顯然這一步并沒(méi)有做什么事!parent顯然是null的,那么就不合并嘛!還是使用當(dāng)前this的環(huán)境。
做個(gè)總結(jié):前六步基本上做了兩件事:
1、初始化相關(guān)上下文環(huán)境,也就是初始化ClassPathXmlApplicationContext實(shí)例
2、獲得一個(gè)resourcePatternResolver對(duì)象,方便第七步的資源解析成Resource對(duì)象
第七步:
第七步又回到剛開(kāi)始第三步的代碼,因?yàn)槲覀兦懊?6 步已經(jīng)完成對(duì)super(parent)的追蹤。讓我們看看setConfigLocation()方法是怎么一回事~
/** * Set the config locations for this application context.//未應(yīng)用上下文設(shè)置資源路徑 *If not set, the implementation may use a default as appropriate.//如果未設(shè)置,則實(shí)現(xiàn)可以根據(jù)需要使用默認(rèn)值。
*/public void setConfigLocations(String... locations) { if (locations != null) {//非空 Assert.noNullElements(locations, "Config locations must not be null");//斷言保證locations的每個(gè)元素都不為null this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim();//去空格,很好奇resolvePath做了什么事情? } } else { this.configLocations = null; }}進(jìn)入resolvePath()方法看看:
/** * 解析給定的資源路徑,必要時(shí)用相應(yīng)的環(huán)境屬性值替換占位符,應(yīng)用于資源路徑配置。 * Resolve the given path, replacing placeholders with corresponding * environment property values if necessary. Applied to config locations. * @param path the original file path * @return the resolved file path * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String) */protected String resolvePath(String path) { return getEnvironment().resolveRequiredPlaceholders(path);}進(jìn)入getEnvironment()看看:/** * {@inheritDoc} *If {@code null}, a new environment will be initialized via
* {@link #createEnvironment()}. */@Overridepublic ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment;}進(jìn)入createEnvironment(), 方法,我們看到在這里創(chuàng)建了一個(gè)新的StandardEnviroment對(duì)象,它是Environment的實(shí)現(xiàn)類,表示容器運(yùn)行的環(huán)境,比如 JDK 環(huán)境,Servlet 環(huán)境,Spring 環(huán)境等等。
每個(gè)環(huán)境都有自己的配置數(shù)據(jù),如System.getProperties()、System.getenv()等可以拿到 JDK 環(huán)境數(shù)據(jù);ServletContext.getInitParameter()可以拿到 Servlet 環(huán)境配置數(shù)據(jù)等等, 也就是說(shuō) Spring 抽象了一個(gè)Environment來(lái)表示環(huán)境配置。
生成的StandardEnviroment對(duì)象并沒(méi)有包含什么內(nèi)容,只是一個(gè)標(biāo)準(zhǔn)的環(huán)境,所有的屬性都是默認(rèn)值。
第八步:這一步是重頭戲
先做個(gè)小結(jié):到現(xiàn)在為止,我們擁有了以下實(shí)例:
現(xiàn)在代碼運(yùn)行到如下圖的refresh()方法:
看一下這個(gè)方法的內(nèi)容是什么?
@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 刷新前準(zhǔn)備工作,包括設(shè)置啟動(dòng)時(shí)間,是否激活標(biāo)識(shí)位,初始化屬性源(property source)配置 prepareRefresh(); // 創(chuàng)建beanFactory(過(guò)程是根據(jù)xml為每個(gè)bean生成BeanDefinition并注冊(cè)到生成的beanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //準(zhǔn)備創(chuàng)建好的beanFactory(給beanFactory設(shè)置ClassLoader,設(shè)置SpEL表達(dá)式解析器,設(shè)置類型轉(zhuǎn)化器【能將xml String類型轉(zhuǎn)成相應(yīng)對(duì)象】, //增加內(nèi)置ApplicationContextAwareProcessor對(duì)象,忽略各種Aware對(duì)象,注冊(cè)各種內(nèi)置的對(duì)賬對(duì)象【BeanFactory,ApplicationContext】等, //注冊(cè)AOP相關(guān)的一些東西,注冊(cè)環(huán)境相關(guān)的一些bean prepareBeanFactory(beanFactory); try { // 模板方法,為容器某些子類擴(kuò)展功能所用(工廠后處理器)這里可以參考BeanFactoryPostProcessor接口的postProcessBeanFactory方法 postProcessBeanFactory(beanFactory); // 調(diào)用所有BeanFactoryPostProcessor注冊(cè)為Bean invokeBeanFactoryPostProcessors(beanFactory); // 注冊(cè)所有實(shí)現(xiàn)了BeanPostProcessor接口的Bean registerBeanPostProcessors(beanFactory); // 初始化MessageSource,和國(guó)際化相關(guān) initMessageSource(); // 初始化容器事件傳播器 initApplicationEventMulticaster(); // 調(diào)用容器子類某些特殊Bean的初始化,模板方法 onRefresh(); // 為事件傳播器注冊(cè)監(jiān)聽(tīng)器 registerListeners(); // 初始化所有剩余的bean(普通bean) finishBeanFactoryInitialization(beanFactory); // 初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 銷毀已創(chuàng)建的bean destroyBeans(); // 重置`active`標(biāo)志 cancelRefresh(ex); throw ex; } finally { //重置一些緩存 resetCommonCaches(); } }}在這里我想說(shuō)一下,這個(gè)refresh()方法其實(shí)是一個(gè)模板方法, 很多方法都讓不同的實(shí)現(xiàn)類去實(shí)現(xiàn),但該類本身也實(shí)現(xiàn)了其中一些方法,并且這些已經(jīng)實(shí)現(xiàn)的方法是不允許子類重寫的,比如:prepareRefresh()方法。更多模板方法設(shè)計(jì)模式,可看我之前的文章?談一談我對(duì)‘模板方法’設(shè)計(jì)模式的理解(Template)。
先進(jìn)入prepareRefresh()方法:
/** * Prepare this context for refreshing, setting its startup date and * active flag as well as performing any initialization of property sources. */protected void prepareRefresh() { this.startupDate = System.currentTimeMillis();//設(shè)置容器啟動(dòng)時(shí)間 this.closed.set(false);//容器關(guān)閉標(biāo)志,是否關(guān)閉? this.active.set(true);//容器激活標(biāo)志,是否激活? if (logger.isInfoEnabled()) {//運(yùn)行到這里,控制臺(tái)就會(huì)打印當(dāng)前容器的信息 logger.info("Refreshing " + this); } // 空方法,由子類覆蓋實(shí)現(xiàn),初始化容器上下文中的property文件 initPropertySources(); //驗(yàn)證標(biāo)記為必需的所有屬性均可解析,請(qǐng)參閱ConfigurablePropertyResolver#setRequiredProperties getEnvironment().validateRequiredProperties(); //允許收集早期的ApplicationEvents,一旦多播器可用,即可發(fā)布... this.earlyApplicationEvents = new LinkedHashSet();}控制臺(tái)輸出:
三月 22, 2018 4:21:13 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@96532d6: startup date [Thu Mar 22 16:21:09 CST 2018]; root of context hierarchy第九步:
進(jìn)入obtainFreshBeanFactory()方法:
/** * 告訴子類刷新內(nèi)部bean工廠(子類是指AbstractApplicationContext的子類,我們使用的是ClassPathXmlApplicationContext) * Tell the subclass to refresh the internal bean factory. */protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory();//刷新Bean工廠,如果已經(jīng)存在Bean工廠,那就關(guān)閉并銷毀,再創(chuàng)建一個(gè)新的bean工廠 ConfigurableListableBeanFactory beanFactory = getBeanFactory();//獲取新創(chuàng)建的Bean工廠 if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);//控制臺(tái)打印 } return beanFactory;}進(jìn)入refreshBeanFactory()方法:/** * 該實(shí)現(xiàn)執(zhí)行該上下文的基礎(chǔ)Bean工廠的實(shí)際刷新,關(guān)閉以前的Bean工廠(如果有的話)以及為該上下文的生命周期的下一階段初始化新鮮的Bean工廠。 * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */@Overrideprotected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) {//如果已有bean工廠 destroyBeans();//銷毀 closeBeanFactory();//關(guān)閉 } try { DefaultListableBeanFactory beanFactory = createBeanFactory();//創(chuàng)建一個(gè)新的bean工廠 beanFactory.setSerializationId(getId());//為序列化目的指定一個(gè)id,如果需要,可以將此BeanFactory從此id反序列化回BeanFactory對(duì)象。 //定制容器,設(shè)置啟動(dòng)參數(shù)(bean可覆蓋、循環(huán)引用),開(kāi)啟注解自動(dòng)裝配 customizeBeanFactory(beanFactory); 將所有BeanDefinition載入beanFactory中,此處依舊是模板方法,具體由子類實(shí)現(xiàn) loadBeanDefinitions(beanFactory); //beanFactory同步賦值 synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); }}總結(jié):這一步主要的工作就是判斷刷新容器前是否已經(jīng)有 beanfactory 存在,如果有,那么就銷毀舊的 beanfactory, 那么就銷毀掉并且創(chuàng)建一個(gè)新的 beanfactory 返回給容器,同時(shí)將 xml 文件的BeanDefinition注冊(cè)到 beanfactory 中。如果不太清楚可以回過(guò)頭看看我們的第三節(jié)第5點(diǎn)內(nèi)容
第十步:
進(jìn)入第九步的loadBeanDefinitions(beanFactory)方法中去take a look:
/** * 使用XmlBeanDefinitionReader來(lái)加載beandefnition,之前說(shuō)過(guò)使用reader機(jī)制加載Resource資源變?yōu)锽eanDefinition對(duì)象 * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 創(chuàng)建XmlBeanDefinitionReader對(duì)象 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 使用當(dāng)前上下文Enviroment中的Resource配置beanDefinitionReader,因?yàn)閎eanDefinitionReader要將Resource解析成BeanDefinition嘛! beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //初始化這個(gè)reader initBeanDefinitionReader(beanDefinitionReader); //將beandefinition注冊(cè)到工廠中(這一步就是將bean保存到Map中) loadBeanDefinitions(beanDefinitionReader);}控制臺(tái)輸出:
三月 22, 2018 5:09:40 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions信息: Loading XML bean definitions from class path resource [applicationContext.xml]第十一步:
進(jìn)入prepareBeanFactory(beanFactory)方法:
//設(shè)置bean類加載器//設(shè)置Spring語(yǔ)言表達(dá)式(SpEL)解析器//掃描ApplicationContextAware bean//注冊(cè)類加載期類型切面織入(AOP)LoadTimeWeaver//為各種加載進(jìn)入beanFactory的bean配置默認(rèn)環(huán)境第十二步:
postProcessBeanFactory(beanFactory)方法:
postProcessBeanFactory同樣作為一個(gè)模板方法,由子類來(lái)提供具體的實(shí)現(xiàn),子類可以有自己的特殊對(duì)BeanDefinition后處理方法,即子類可以在這對(duì)前面生成的BeanDefinition,即bean的元數(shù)據(jù)再處理。比如修改某個(gè)bean的id/name屬性、scope屬性、lazy-init屬性等。
第十三步:
invokeBeanFactoryPostProcessors(beanFactory)方法:
該方法調(diào)用所有的BeanFactoryPostProcessor,它是一個(gè)接口,實(shí)現(xiàn)了此接口的類需重寫postProcessBeanFactory()這個(gè)方法,可以看出該方法跟第十二步的方法是一樣的,只不過(guò)作為接口,更多的是提供給開(kāi)發(fā)者來(lái)對(duì)生成的BeanDefinition做處理,由開(kāi)發(fā)者提供處理邏輯。
第十四步:
其余剩下的方法基本都是像初始化消息處理源,初始化容器事件,注冊(cè)bean監(jiān)聽(tīng)器到事件傳播器上,最后完成容器刷新。
五、總結(jié)
恭喜我,我終于寫完了,同樣也恭喜你,你也閱讀完了。
我很佩服我自己能花這么長(zhǎng)時(shí)間進(jìn)行總結(jié)發(fā)布,之所以要進(jìn)行總結(jié),那是因?yàn)樾【庍€是贊同好記性不如爛筆頭的說(shuō)法。
你不記,你過(guò)陣子就會(huì)忘記,你若記錄,你過(guò)陣子也會(huì)忘記!區(qū)別在于忘記了,可以回過(guò)頭在很短的時(shí)間內(nèi)進(jìn)行回憶,查漏補(bǔ)缺,減少學(xué)習(xí)成本。
再者,我認(rèn)為我分析的還不是完美的,缺陷很多,因此我將我寫的所有文章發(fā)布出來(lái)和大家探討交流,汕頭大學(xué)有校訓(xùn)說(shuō)得非常地好,那就是說(shuō)知識(shí)是用來(lái)共享的,因?yàn)楣蚕砹?#xff0c;知識(shí)才能承前啟后。
現(xiàn)在再梳理一下 Spring 初始化過(guò)程:
1、首先初始化上下文,生成ClassPathXmlApplicationContext對(duì)象,在獲取resourcePatternResolver對(duì)象將xml解析成Resource對(duì)象。
2、利用 1 生成的 context、resource 初始化工廠,并將 resource 解析成 beandefinition, 再將 beandefinition 注冊(cè)到 beanfactory 中。
推薦閱讀1、Spring Boot+Vue項(xiàng)目實(shí)戰(zhàn)
2、B站:4小時(shí)上手MyBatis Plus
3、一文搞懂前后端分離
4、快速上手Spring Boot+Vue前后端分離
楠哥簡(jiǎn)介
資深 Java 工程師,微信號(hào)?southwindss
《Java零基礎(chǔ)實(shí)戰(zhàn)》一書作者
騰訊課程官方 Java 面試官,今日頭條認(rèn)證大V
GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)
致力于幫助萬(wàn)千 Java 學(xué)習(xí)者持續(xù)成長(zhǎng)。
有收獲,就點(diǎn)個(gè)在看總結(jié)
以上是生活随笔為你收集整理的spring图片转视频_一直在用的 Spring,你知道它的加载原理吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: jdbc 自增id 原理_面试被问分布式
- 下一篇: python程序写蛇_python蟒蛇绘