亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Bean 中的生命周期和獲取方式詳解

 更新時(shí)間:2024年12月07日 10:46:22   作者:程序猿進(jìn)階  
Bean的加載和獲取過(guò)程涉及配置文件解析、資源加載、XML解析和BeanDefinition注冊(cè)等步驟,本文給大家介紹Spring Bean 的生命周期和獲取方式,感興趣的朋友跟隨小編一起看看吧

一、Spring Bean 的生命周期,如何被管理的

對(duì)于普通的 Java對(duì)象,當(dāng) new的時(shí)候創(chuàng)建對(duì)象,當(dāng)它沒(méi)有任何引用的時(shí)候被垃圾回收機(jī)制回收。而由 Spring IoC容器托管的對(duì)象,它們的生命周期完全由容器控制。Spring 中每個(gè) Bean的生命周期如下:

主要對(duì)幾個(gè)重要的步驟進(jìn)行說(shuō)明:
【1】實(shí)例化 Bean: 對(duì)于 BeanFactory容器,當(dāng)客戶(hù)向容器請(qǐng)求一個(gè)尚未初始化的 bean時(shí),或初始化 bean的時(shí)候需要注入另一個(gè)尚未初始化的依賴(lài)時(shí),容器就會(huì)調(diào)用 createBean進(jìn)行實(shí)例化。對(duì)于 ApplicationContext容器,當(dāng)容器啟動(dòng)結(jié)束后,便實(shí)例化所有的單實(shí)例 bean。容器通過(guò)獲取 BeanDefinition對(duì)象中的信息進(jìn)行實(shí)例化。并且這一步僅僅是簡(jiǎn)單的實(shí)例化,并未進(jìn)行依賴(lài)注入。實(shí)例化對(duì)象被包裝在 BeanWrapper 對(duì)象中,BeanWrapper 提供了設(shè)置對(duì)象屬性的接口,從而避免了使用反射機(jī)制設(shè)置屬性。通過(guò)工廠(chǎng)方法或者執(zhí)行構(gòu)造器解析執(zhí)行即可:創(chuàng)建的對(duì)象是個(gè)空對(duì)象。
【2】設(shè)置對(duì)象屬性(依賴(lài)注入): 實(shí)例化后的對(duì)象被封裝在 BeanWrapper對(duì)象中,并且此時(shí)對(duì)象仍然是一個(gè)原生的狀態(tài),并沒(méi)有進(jìn)行依賴(lài)注入。緊接著獲取所有的屬性信息通過(guò) populateBean(beanName,mbd,bw,pvs),Spring 根據(jù) BeanDefinition 中的信息進(jìn)行依賴(lài)注入。并且通過(guò) BeanWrapper提供的設(shè)置屬性的接口完成依賴(lài)注入。賦值之前獲取所有的 InstantiationAwareBeanPostProcessor 后置處理器的 postProcessAfterInstantiation() 第二次獲取InstantiationAwareBeanPostProcessor 后置處理器;執(zhí)行 postProcessPropertyValues()最后為應(yīng)用 Bean屬性賦值:為屬性利用 setter 方法進(jìn)行賦值 applyPropertyValues(beanName,mbd,bw,pvs)。
【3】bean 初始化: initializeBean(beanName,bean,mbd)。
 1)執(zhí)行xxxAware 接口的方法,調(diào)用實(shí)現(xiàn)了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口的方法。
 2)執(zhí)行后置處理器之前的方法:applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)所有后置處理器的 BeanPostProcessor.postProcessBeforeInitialization()。
 3)執(zhí)行初始化方法: InitializingBean 與 init-methodinvoke 當(dāng) BeanPostProcessor的前置處理完成后就會(huì)進(jìn)入本階段。先判斷是否實(shí)現(xiàn)了 InitializingBean接口的實(shí)現(xiàn);執(zhí)行接口規(guī)定的初始化。其次自定義初始化方法。

InitializingBean 接口只有一個(gè)函數(shù):afterPropertiesSet()這一階段也可以在 bean正式構(gòu)造完成前增加我們自定義的邏輯,但它與前置處理不同,由于該函數(shù)并不會(huì)把當(dāng)前 bean對(duì)象傳進(jìn)來(lái),因此在這一步?jīng)]辦法處理對(duì)象本身,只能增加一些額外的邏輯。若要使用它,我們需要讓 bean實(shí)現(xiàn)該接口,并把要增加的邏輯寫(xiě)在該函數(shù)中。然后 Spring會(huì)在前置處理完成后檢測(cè)當(dāng)前 bean是否實(shí)現(xiàn)了該接口,并執(zhí)行 afterPropertiesSet函數(shù)。當(dāng)然,Spring 為了降低對(duì)客戶(hù)代碼的侵入性,給 bean的配置提供了 init-method屬性,該屬性指定了在這一階段需要執(zhí)行的函數(shù)名。Spring 便會(huì)在初始化階段執(zhí)行我們?cè)O(shè)置的函數(shù)。init-method 本質(zhì)上仍然使用了InitializingBean接口。
 4)applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);執(zhí)行初始化之后的后置處理器的方法。BeanPostProcessor.postProcessAfterInitialization(result, beanName);
【4】Bean的銷(xiāo)毀: DisposableBean 和 destroy-method:和 init-method 一樣,通過(guò)給 destroy-method 指定函數(shù),就可以在bean 銷(xiāo)毀前執(zhí)行指定的邏輯。

Bean 的管理就是通過(guò) IOC容器中的 BeanDefinition信息進(jìn)行管理的。

二、Spring Bean 的加載和獲取過(guò)程

Bean 的加載過(guò)程,主要是對(duì)配置文件的解析,并注冊(cè) bean 的過(guò)程 。

【1】根據(jù)注解或者 XML中 定義 Bean 的基本信息。例如:spring-core.xml

<bean id="myBean" class="com.taobao.pojo"></bean>

【2】獲取配置文件:這里使用最原始的方式獲取。

Resource resource = new ClassPathResource("spring-core.xml")

【3】 利用 XmlBeanFactory 解析并注冊(cè) bean 定義:已經(jīng)完成將配置文件包裝成了 Spring 定義的資源,并觸發(fā)解析和注冊(cè)。XmlBeanFactory 實(shí)際上是對(duì) DefaultListableBeanFactory(非常核心的類(lèi),它包含了基本 IOC 容器所具有的重要功能,是一個(gè) IOC 容器的基本實(shí)現(xiàn)。然后是調(diào)用了this.reader.loadBeanDefinitions(resource),從這里開(kāi)始加載配置文件) 和 XmlBeanDefinitionReader 組合使用方式的封裝,所以這里我們?nèi)匀粚⒗^續(xù)分析基于 XmlBeanFactory 加載 bean 的過(guò)程。

XmlBeanFactory beanFactory = new XmlBeanFactory(resource);

Spring 使用了專(zhuān)門(mén)的資源加載器對(duì)資源進(jìn)行加載,這里的 reader 就是 XmlBeanDefinitionReader 對(duì)象,專(zhuān)門(mén)用來(lái)加載基于 XML 文件配置的 bean。這里的加載過(guò)程為:

①、利用 EncodedResource 二次包裝資源文件;
②、獲取資源輸入流,并構(gòu)造 InputSource 對(duì)象:

// 獲取資源的輸入流
InputStream inputStream = encodedResource.getResource().getInputStream();
// 構(gòu)造InputSource對(duì)象
InputSource inputSource = new InputSource(inputStream);
// 真正開(kāi)始從 XML文件中加載 Bean定義
return this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());

這里的 this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 就是真正開(kāi)始加載 XMl 的入口,該方法源碼如下:第一步獲取 org.w3c.dom.Document 對(duì)象,第二步由該對(duì)象解析得到 BeanDefinition 對(duì)象,并注冊(cè)到 IOC 容器中。

protected intdoLoadBeanDefinitions(InputSource inputSource, Resource resource){
  try {
    // 1. 加載xml文件,獲取到對(duì)應(yīng)的Document(包含獲取xml文件的實(shí)體解析器和驗(yàn)證模式)
    Document doc = this.doLoadDocument(inputSource, resource);
    // 2. 解析Document對(duì)象,并注冊(cè)bean
    return this.registerBeanDefinitions(doc, resource);
  } 
}

③、獲取 XML 文件的實(shí)體解析器和驗(yàn)證模式:this.doLoadDocument(inputSource, resource) 包含了獲取實(shí)體解析器、驗(yàn)證模式,以及 Document 對(duì)象的邏輯,XML 是半結(jié)構(gòu)化數(shù)據(jù),XML 的驗(yàn)證模式用于保證結(jié)構(gòu)的正確性,常見(jiàn)的驗(yàn)證模式有 DTD 和 XSD 兩種。
④、加載 XML 文件,獲取對(duì)應(yīng)的 Document 對(duì)象和驗(yàn)證模式與解析器,解析器就可以加載 Document 對(duì)象了,這里本質(zhì)上調(diào)用的是 DefaultDocumentLoader 的 loadDocument() 方法,源碼如下:整個(gè)過(guò)程類(lèi)似于我們平常解析 XML 文件的流程。

public DocumentloadDocument(InputSource inputSource, EntityResolver entityResolver,
               ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
    DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

⑤、由 Document 對(duì)象解析并注冊(cè) bean:完成了對(duì) XML 文件的到 Document 對(duì)象的解析,我們終于可以解析 Document 對(duì)象,并注冊(cè) bean 了,這一過(guò)程發(fā)生在 this.registerBeanDefinitions(doc, resource) 中,源碼如下:

public intregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{
    // 使用DefaultBeanDefinitionDocumentReader構(gòu)造
    BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
    // 記錄之前已經(jīng)注冊(cè)的BeanDefinition個(gè)數(shù)
    int countBefore = this.getRegistry().getBeanDefinitionCount();
    // 加載并注冊(cè)bean
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // 返回本次加載的bean的數(shù)量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

這里方法的作用是創(chuàng)建對(duì)應(yīng)的 BeanDefinitionDocumentReader,并計(jì)算返回了過(guò)程中新注冊(cè)的 bean 的數(shù)量,而具體的注冊(cè)過(guò)程,則是由 BeanDefinitionDocumentReader 來(lái)完成的,具體的實(shí)現(xiàn)位于子類(lèi) DefaultBeanDefinitionDocumentReader 中:

publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){
    this.readerContext = readerContext;
    // 獲取文檔的root結(jié)點(diǎn)
    Element root = doc.getDocumentElement();
    this.doRegisterBeanDefinitions(root);
}

還是按照 Spring 命名習(xí)慣,doRegisterBeanDefinitions 才是真正干活的地方,這也是真正開(kāi)始解析配置的核心所在:

protectedvoiddoRegisterBeanDefinitions(Element root){
  BeanDefinitionParserDelegate parent = this.delegate;
  this.delegate = this.createDelegate(getReaderContext(), root, parent);
    // 處理profile標(biāo)簽(其作用類(lèi)比pom.xml中的profile)
    String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    // 解析預(yù)處理,留給子類(lèi)實(shí)現(xiàn)
    this.preProcessXml(root);
    // 解析并注冊(cè)BeanDefinition
    this.parseBeanDefinitions(root, this.delegate);
    // 解析后處理,留給子類(lèi)實(shí)現(xiàn)
    this.postProcessXml(root);
}

方法在解析并注冊(cè) BeanDefinition 前后各設(shè)置一個(gè)模板方法,留給子類(lèi)擴(kuò)展實(shí)現(xiàn),而在this.parseBeanDefinitions(root, this.delegate)中執(zhí)行解析和注冊(cè)邏輯:方法中判斷當(dāng)前標(biāo)簽是默認(rèn)標(biāo)簽還是自定義標(biāo)簽,并按照不同的策略去解析。

protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){
    if (delegate.isDefaultNamespace(root)) {
        // 解析默認(rèn)標(biāo)簽
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                  // 解析默認(rèn)標(biāo)簽
                  this.parseDefaultElement(ele, delegate);
                } else {
                  // 解析自定義標(biāo)簽
                  delegate.parseCustomElement(ele);
                }
            }
        }
      } else {
        // 解析自定義標(biāo)簽
        delegate.parseCustomElement(root);
   }
}

到這里我們已經(jīng)完成了靜態(tài)配置到動(dòng)態(tài) BeanDefinition 的解析,這個(gè)時(shí)候 bean 的定義已經(jīng)處于內(nèi)存中。

【4】 從 IOC容器加載獲取 bean:我們可以調(diào)用 beanFactory.getBean("myBean") 方法來(lái)獲取目標(biāo)對(duì)象。

MyBean myBean = (MyBean) beanFactory.getBean("myBean");

到此這篇關(guān)于Spring Bean 的生命周期和獲取方式詳解的文章就介紹到這了,更多相關(guān)Spring Bean 生命周期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Idea實(shí)現(xiàn)接口的方法上無(wú)法添加@Override注解的解決方案

    Idea實(shí)現(xiàn)接口的方法上無(wú)法添加@Override注解的解決方案

    文章介紹了在IDEA中實(shí)現(xiàn)接口方法時(shí)無(wú)法添加@Override注解的問(wèn)題及其解決方法,主要步驟包括更改項(xiàng)目結(jié)構(gòu)中的Language level到支持該注解的版本,以及在pom.xml文件中指定maven-compiler-plugin的版本以解決自動(dòng)更新后的問(wèn)題
    2025-02-02
  • Java?如何用二維數(shù)組創(chuàng)建空心菱形

    Java?如何用二維數(shù)組創(chuàng)建空心菱形

    這篇文章主要介紹了Java?如何用二維數(shù)組創(chuàng)建空心菱形,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 詳解java如何調(diào)用代理ip

    詳解java如何調(diào)用代理ip

    在進(jìn)行網(wǎng)絡(luò)爬蟲(chóng)、數(shù)據(jù)采集或是訪(fǎng)問(wèn)網(wǎng)站時(shí),使用代理IP顯得尤為重要,本文將深入探討如何在Java中調(diào)用代理IP,感興趣的小伙伴可以了解一下
    2024-11-11
  • 使用Java獲取文件樹(shù)的代碼實(shí)現(xiàn)

    使用Java獲取文件樹(shù)的代碼實(shí)現(xiàn)

    Java語(yǔ)言提供了豐富的庫(kù)和工具,使得我們可以方便地獲取和操作Java文件的語(yǔ)法樹(shù)(AST, Abstract Syntax Tree),在這篇博客中,我們將探討如何使用Java來(lái)獲取一個(gè)Java文件的語(yǔ)法樹(shù),并展示詳細(xì)的代碼示例和運(yùn)行結(jié)果,需要的朋友可以參考下
    2024-08-08
  • Java實(shí)現(xiàn)JSON與XML相互轉(zhuǎn)換的簡(jiǎn)明教程

    Java實(shí)現(xiàn)JSON與XML相互轉(zhuǎn)換的簡(jiǎn)明教程

    Java實(shí)現(xiàn)復(fù)雜數(shù)據(jù)結(jié)構(gòu)(如嵌套對(duì)象、數(shù)組)在 JSON 與 XML 之間的相互轉(zhuǎn)換,可以使用 Jackson 和 Jackson XML 擴(kuò)展庫(kù)來(lái)完成,Jackson 是一個(gè)流行的 JSON 處理庫(kù),通過(guò) Jackson 的 XML 擴(kuò)展庫(kù),可以實(shí)現(xiàn) JSON 和 XML 之間的轉(zhuǎn)換,需要的朋友可以參考下
    2024-08-08
  • java操作mysql實(shí)現(xiàn)增刪改查的方法

    java操作mysql實(shí)現(xiàn)增刪改查的方法

    這篇文章主要介紹了java操作mysql實(shí)現(xiàn)增刪改查的方法,結(jié)合實(shí)例形式分析了java操作mysql數(shù)據(jù)庫(kù)進(jìn)行增刪改查的具體實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-05-05
  • Spring Security角色繼承實(shí)現(xiàn)過(guò)程解析

    Spring Security角色繼承實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了Spring Security角色繼承實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java獲取任意http網(wǎng)頁(yè)源代碼的方法

    Java獲取任意http網(wǎng)頁(yè)源代碼的方法

    這篇文章主要介紹了Java獲取任意http網(wǎng)頁(yè)源代碼的方法,可實(shí)現(xiàn)獲取網(wǎng)頁(yè)代碼以及去除HTML標(biāo)簽的代碼功能,涉及Java正則操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • java金額數(shù)字轉(zhuǎn)中文工具類(lèi)詳解

    java金額數(shù)字轉(zhuǎn)中文工具類(lèi)詳解

    這篇文章主要為大家詳細(xì)介紹了java金額數(shù)字轉(zhuǎn)中文工具類(lèi)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • jdbc連接數(shù)據(jù)庫(kù)實(shí)例詳解

    jdbc連接數(shù)據(jù)庫(kù)實(shí)例詳解

    在本篇內(nèi)容里小編給大家分享了關(guān)于jdbc如何連接數(shù)據(jù)庫(kù)的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們學(xué)習(xí)下。
    2019-02-02

最新評(píng)論