国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

(spring-第5回【IoC基礎(chǔ)篇】)spring容器從加載配置文件到實(shí)例化bean的內(nèi)部工作機(jī)制

2019-11-14 15:04:08
字體:
供稿:網(wǎng)友

前面講過,sPRing的生命周期為:實(shí)例化前奏-->實(shí)例化-->實(shí)例化后期-->初始化前期-->初始化-->初始化后期-->bean的具體調(diào)用-->銷毀前-->銷毀。那么,從裝配xml屬性到實(shí)例化bean的內(nèi)部機(jī)制是怎樣的,沒有細(xì)說,今天我們來一起刨根問底。

還是老風(fēng)格,以具體例子先入為主。下面是一個(gè)再簡(jiǎn)單不過的spring框架的栗子。(XML,有。Bean,有。Spring容器,有。main函數(shù),有。麻雀雖小,但是夠了。)

這是XML,簡(jiǎn)單易懂,嘎嘣脆:

1 。。。。。。2 3 <bean id="car" class="com.mesopotamia.test1.Car" 4          p:brand="寶馬X5"5          p:maxSpeed="200"/>6 </beans>

這是Bean,要個(gè)子有個(gè)子,要西一翁有西一翁:

代碼001

1
public class Car { 2 private String name; 3 private String brand; 4 private double maxSpeed; 5 public double getMaxSpeed() { 6 return maxSpeed; 7 } 8 public void setMaxSpeed(double maxSpeed) { 9 this.maxSpeed = maxSpeed;10 }11 12 13 private Log log=LogFactory.getLog(Car.class);14 15 public Car(){16 name="寶馬";17 log.info("調(diào)用了Car的構(gòu)造函數(shù),實(shí)例化了Car,并把Car的name屬性設(shè)為:"+name);18 }19 public String getName() {20 return name;21 }22 public void setName(String name) {23 this.name = name;24 }25 public String getBrand() {26 return brand;27 }28 public void setBrand(String brand) {29 this.brand = brand;30 }31 32 33 public String toString(){34 return "名字"+name+" 型號(hào)"+" 速度:"+maxSpeed;35 }36 37 38 }

下面是啟動(dòng)函數(shù),精悍!干練:

代碼002

1
public class Main {2 private static Log log=LogFactory.getLog(Main.class);3 4 public static void main(String args[]){5 applicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/test1/*.xml");6 Car car1 = ctx.getBean("car",Car.class);7 log.info(car1.toString());8 }

但是我要講的重點(diǎn)是main函數(shù)跑起來后的日志:

代碼003

1
2015-11-16 20:19:04,318 INFO [main] (AbstractApplicationContext.java:456) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1ff5ea7: startup date [Mon Nov 16 20:19:04 CST 2015]; root of context hierarchy2 2015-11-16 20:19:04,371 INFO [main] (XmlBeanDefinitionReader.java:315) - Loading XML bean definitions from file [C:/MySoftware/workspace/springtest/WebRoot/WEB-INF/classes/com/mesopotamia/test1/beans.xml]3 2015-11-16 20:19:04,482 INFO [main] (DefaultListableBeanFactory.java:555) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6e293a: defining beans [car]; root of factory hierarchy4 2015-11-16 20:19:04,483 INFO [main] (Car.java:22) - 調(diào)用了Car的構(gòu)造函數(shù),實(shí)例化了Car,并把Car的name屬性設(shè)為:寶馬5 2015-11-16 20:19:04,533 INFO [main] (Main.java:15) - 名字寶馬 型號(hào) 速度:200.0

一開始就加載了AbstractApplicationContext里的方法,那么這個(gè)方法做了什么?實(shí)際上,第一行是由AbstractApplicationContext的refresh()方法打印出的,容器一啟動(dòng)就要加載這個(gè)方法,讓我們來揭開refresh()的面紗吧。

首先,這個(gè)AbstractApplicationContext必須是ClassPathXmlApplicationContext的父類,否則代碼002的第5行怎么一跑起來會(huì)跑到AbstractApplicationContext里面的方法里去?孩子被打,當(dāng)然是去叫爹咯。

在MyEclipse里,我們按住ctrl鍵,點(diǎn)擊ClassPathXmlApplicationContext一路點(diǎn)下去你就發(fā)現(xiàn)其中的繼承關(guān)系(姑且用—>表示子類指向被繼承的父類):

ClassPathXmlApplicationContext —> AbstractXmlApplicationContext —>AbstractRefreshableConfigApplicationContext —>AbstractRefreshableApplicationContext—>AbstractApplicationContext。

我們進(jìn)入AbstractApplicationContext,看到refresh()方法的廬山真面目:

 1 public void refresh() throws BeansException, IllegalStateException { 2         synchronized (this.startupShutdownMonitor) { 3             // Prepare this context for refreshing. 4             prepareRefresh(); 5  6             // Tell the subclass to refresh the internal bean factory. 7             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 8  9             // Prepare the bean factory for use in this context.10             prepareBeanFactory(beanFactory);11 12             try {13                 // Allows post-processing of the bean factory in context subclasses.14                 postProcessBeanFactory(beanFactory);15 16                 // Invoke factory processors registered as beans in the context.17                 invokeBeanFactoryPostProcessors(beanFactory);18 19                 // Register bean processors that intercept bean creation.20                 registerBeanPostProcessors(beanFactory);21 22                 // Initialize message source for this context.23                 initMessageSource();24 25                 // Initialize event multicaster for this context.26                 initApplicationEventMulticaster();27 28                 // Initialize other special beans in specific context subclasses.29                 onRefresh();30 31                 // Check for listener beans and register them.32                 registerListeners();33 34                 // Instantiate all remaining (non-lazy-init) singletons.35                 finishBeanFactoryInitialization(beanFactory);36 37                 // Last step: publish corresponding event.38                 finishRefresh();39             }40 41             catch (BeansException ex) {42                 // Destroy already created singletons to avoid dangling resources.43                 destroyBeans();44 45                 // Reset 'active' flag.46                 cancelRefresh(ex);47 48                 // Propagate exception to caller.49                 throw ex;50             }51         }52     }

人生若只如初見,看到這如此美麗的代碼是否驚呆了天真爛漫的你?OK,我知道你英語沒我好,我就大概講一下這refresh()里面都干了些什么,這些步驟實(shí)際上就是實(shí)例化之前的一系列美麗動(dòng)作:

第9行:準(zhǔn)備bean factory  (BeanFactory是spring框架的基礎(chǔ)設(shè)施)

第16行:調(diào)用被注冊(cè)為bean的BeanFactoryPostProcessors(工廠后處理器)  (工廠后處理器負(fù)責(zé)對(duì)實(shí)例化之前未成形的bean進(jìn)行加工處理)

第19行:注冊(cè)BeanPostProcessors(Bean后處理器)來阻擋bean的創(chuàng)建。  (實(shí)例化后的bean需要用這個(gè)后處理器來進(jìn)一步加工)

第22行:初始化消息源(國際化信息資源)。  (國際化很容易理解吧?比如微信可以切換中英文版本等等,后面會(huì)獨(dú)列篇幅講解)

第25行:初始化應(yīng)用上下文事件廣播器。  (spring有一套完善的事件發(fā)布和監(jiān)聽機(jī)制,事件廣播器負(fù)責(zé)把事件通知給監(jiān)聽器,監(jiān)聽器來執(zhí)行事件。后面會(huì)獨(dú)列篇幅講解)。

第28行:初始化其他特殊的bean。

第31行:檢查是否有監(jiān)聽器然后注冊(cè)監(jiān)聽器(監(jiān)聽器就跟bean一樣,需要放在注冊(cè)表中。后面會(huì)獨(dú)列篇幅講解)。

第34行:初始化所有單實(shí)例的bean(懶模式bean除外),單實(shí)例的bean初始化后把bean的引用放在spring容器的緩存中,調(diào)用者使用的是同一個(gè)額引用,任何一個(gè)調(diào)用者對(duì)bean的修改都會(huì)影響其他調(diào)用者。懶模式是指,spring容器啟動(dòng)時(shí)不會(huì)初始化,而在需要用到該bean時(shí)才初始化。XML<bean>標(biāo)簽中的lazy-init屬性就是設(shè)置懶模式或者勤快模式的,false是勤快模式,true是懶模式。

第37行:創(chuàng)建上下文刷新事件,發(fā)布廣播器。  (事件機(jī)制后面會(huì)獨(dú)列篇幅講解)

 

上面這些就是bean實(shí)例化前后的細(xì)枝末節(jié)了。那么上面的什么工廠后處理器,bean后處理器被調(diào)用后又是怎樣處理的呢?

下面我們來細(xì)化一下創(chuàng)建一個(gè)完整的bean的作業(yè)流程:

整體是下面這樣的:

簡(jiǎn)單點(diǎn):

讀取XML,轉(zhuǎn)化并加工成BeanDefinition,實(shí)例化BeanDefinition。

具體點(diǎn):

ResourceLoader加載XML配置信息后,由BeanDefinitionReader讀取配置信息文件,把每個(gè)<bean>解析成BeanDefinition對(duì)象保存在注冊(cè)表中。容器首先掃描注冊(cè)表取出工廠后處理器,對(duì)注冊(cè)表中的BeanDefinition進(jìn)行加工處理。Spring容器接著從注冊(cè)表中取出加工過的BeanDefinition開始著手bean實(shí)例化的事情。實(shí)例化時(shí),首先由BeanWapper對(duì)bean進(jìn)行封裝,配置屬性。最后利用注冊(cè)表中的Bean后處理器裝飾打扮,裝配出一個(gè)準(zhǔn)備就緒的Bean來。(注冊(cè)表就類似于一個(gè)Map<K,V>,把所有的bean,不管是業(yè)務(wù)bean,還是spring自己的bean,都放到注冊(cè)表里,用的時(shí)候取出來)。

 

再具體點(diǎn):

  1. ResourceLoader加載XML配置信息后,由BeanDefinitionReader讀取配置信息文件,把每個(gè)<bean>解析成BeanDefinition對(duì)象保存在BeanDefinitionRegistry注冊(cè)表中。這時(shí)的BeanDefinition可能只是個(gè)半成品,因?yàn)槟承ML屬性配置里會(huì)有占位符變量,這些變量此時(shí)不會(huì)被解析出來,需要繼續(xù)優(yōu)化,比如下面這樣:
    1    <bean id="simpleBean" class="com.spring.ch04.SimplePostProcessor">  2    <property name="connectionString" value="${simpleBean.connectionString}"/>  3    <property name="passWord" value="${simpleBean.password}"/>  4    <property name="username" value="${simpleBean.username}"/>  5      6    </bean>  

     

  2. 因?yàn)橛?的情況出現(xiàn),所以容器首先掃描注冊(cè)表取出工廠后處理器,對(duì)注冊(cè)表中的BeanDefinition進(jìn)行加工處理,把占位符替換成真正的值,產(chǎn)生成品的BeanDefinition。
  3. 通過反射機(jī)制掃描BeanDefinitionRegistry所有屬性編輯器的bean類,并把這些bean放到spring容器的屬性編輯器注冊(cè)表(PropertyEditorRegistry)中。(Spring的屬性編輯器負(fù)責(zé)將配置文件中的文本配置值轉(zhuǎn)換為Bean屬性的配置值,這個(gè)后面會(huì)獨(dú)辟章節(jié)來講)。
  4. Spring容器從BeanDefinitionRegistry中取出加工后的BeanDefinition,并調(diào)用InstantiationStrategy著手對(duì)bean的實(shí)例化工作。(這里的實(shí)例化只是相當(dāng)于new了一個(gè)新對(duì)象一樣,也就是說,只是跑一個(gè)構(gòu)造函數(shù),不會(huì)具體的為屬性設(shè)置值,當(dāng)然如果構(gòu)造函數(shù)里寫了設(shè)置值的語句,那么也可以賦值。比如一開始的那個(gè)例子,實(shí)例化時(shí)在構(gòu)造函數(shù)里就給Car的name屬性附上了"寶馬"的名字)。
  5. 實(shí)例化的過程中,spring容器使用BeanWrapper對(duì)bean進(jìn)行封裝,BeanWrapper結(jié)合BeanDefinition和屬性編輯器注冊(cè)表中的屬性編輯器完成bean的屬性設(shè)置工作。
  6. 最后調(diào)用Bean后處理器對(duì)bean 作最后的潤色。

 

話音到此戛然而止。洗洗睡吧。

 

  知者不惑,仁者不憂,勇者不懼。

               ----子曰

 


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 峨眉山市| 乐安县| 彭山县| 临夏市| 建湖县| 汉川市| 汉中市| 新密市| 淮北市| 汝南县| 阳春市| 鄂州市| 丹棱县| 本溪市| 成都市| 绵竹市| 恩平市| 内丘县| 牟定县| 金平| 大化| 五莲县| 玉田县| 西乌| 定襄县| 建德市| 黔江区| 宣汉县| 长宁区| 华坪县| 曲水县| 于田县| 临城县| 五大连池市| 靖边县| 承德市| 南木林县| 仲巴县| 威信县| 百色市| 台湾省|