欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解析spring加載bean流程的方法

 更新時間:2021年05月12日 12:06:49   作者:Yrion  
這篇文章主要介紹了解析spring加載bean流程的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

spring作為目前我們開發(fā)的基礎框架,每天的開發(fā)工作基本和他形影不離,作為管理bean的最經典、優(yōu)秀的框架,它的復雜程度往往令人望而卻步。不過作為朝夕相處的框架,我們必須得明白一個問題就是spring是如何加載bean的,我們常在開發(fā)中使用的注解比如@Component、@AutoWired、@Socpe等注解,Spring是如何解析的,明白這些原理將有助于我們更深刻的理解spring。需要說明一點的是spring的源碼非常精密、復雜,限于篇幅的關系,本篇博客不會細致的分析源碼,會采取抽絲剝繭的方式,避輕就重,抓住重點來分析整個流程(不會分析具體的細節(jié)),本次將會基于spring5.0的版本

一:spring讀取配置或注解的過程

1:先通過掃描指定包路徑下的spring注解,比如@Component、@Service、@Lazy @Sope等spring識別的注解或者是xml配置的屬性(通過讀取流,解析成Document,Document)然后spring會解析這些屬性,將這些屬性封裝到BeanDefintaion這個接口的實現(xiàn)類中.

在springboot中,我們也可以采用注解配置的方式:

比如這個配置Bean,spring也會將className、scope、lazy等這些屬性裝配到PersonAction對應的BeanDefintaion中.具體采用的是BeanDefinitionParser接口中的parse(Element element, ParserContext parserContext)方法,該接口有很多不同的實現(xiàn)類。通過實現(xiàn)類去解析注解或者xml然后放到BeanDefination中,BeanDefintaion的作用是集成了我們的配置對象中的各種屬性,重要的有這個bean的ClassName,還有是否是Singleton、對象的屬性和值等(如果是單例的話,后面會將這個單例對象放入到spring的單例池中)。spring后期如果需要這些屬性就會直接從它中獲取。然后,再注冊到一個ConcurrentHashMap中,在spring中具體的方法就是registerBeanDefinition(),這個Map存的key是對象的名字,比如Person這個對象,它的名字就是person,值是BeanDefination,它位于DefaultListableBeanFactory類下面的beanDefinitionMap類屬性中,同時將所有的bean的名字放入到beanDefinitionNames這個list中,目的就是方便取beanName;

二:spring的bean的生命周期

spring的bean生命周期其實最核心的分為4個步驟,只要理清三個關鍵的步驟,其他的只是在這三個細節(jié)中添加不同的細節(jié)實現(xiàn),也就是spring的bean生明周期:

實例化和初始化的區(qū)別:實例化是在jvm的堆中創(chuàng)建了這個對象實例,此時它只是一個空的對象,所有的屬性為null。而初始化的過程就是講對象依賴的一些屬性進行賦值之后,調用某些方法來開啟一些默認加載。比如spring中配置的數據庫屬性Bean,在初始化的時候就會將這些屬性填充,比如driver、jdbcurl等,然后初始化連接

2.1:實例化 Instantiation

AbstractAutowireCapableBeanFactory.doCreateBean中會調用createBeanInstance()方法,該階段主要是從beanDefinitionMap循環(huán)讀取bean,獲取它的屬性,然后利用反射(core包下有ReflectionUtil會先強行將構造方法setAccessible(true))讀取對象的構造方法(spring會自動判斷是否是有參數還是無參數,以及構造方法中的參數是否可用),然后再去創(chuàng)建實例(newInstance)

2.2:初始化

初始化主要包括兩個步驟,一個是屬性填充,另一個就是具體的初始化過程

2.2.1:屬性賦值 PopulateBean()會對bean的依賴屬性進行填充,@AutoWired注解注入的屬性就發(fā)生這個階段,假如我們的bean有很多依賴的對象,那么spring會依次調用這些依賴的對象進行實例化,注意這里可能會有循環(huán)依賴的問題。后面我們會講到spring是如何解決循環(huán)依賴的問題

2.2.2:初始化 Initialization

初始化的過程包括將初始化好的bean放入到spring的緩存中、填充我們預設的屬性進一步做后置處理等

3: 使用和銷毀 Destruction

在Spring將所有的bean都初始化好之后,我們的業(yè)務系統(tǒng)就可以調用了。而銷毀主要的操作是銷毀bean,主要是伴隨著spring容器的關閉,此時會將spring的bean移除容器之中。此后spring的生命周期到這一步徹底結束,不再接受spring的管理和約束。

三:spring的BeanPostProcessor處理器

spring的另一個強大之處就是允許開發(fā)者自定義擴展bean的初始化過程,最主要的實現(xiàn)思路就是通過BeanPostProcessor來實現(xiàn)的,spring有各種前置和后置處理器,這些處理器滲透在bean創(chuàng)建的前前后后,穿插在spring生命周期的各個階段,每一步都會影響著spring的bean加載過程。接下來我們就來分析具體的過程:

3.1:實例化階段

該階段會調用對象的空構造方法進行對象的實例化,在進行實例化之后,會調用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法

BeanPostProcessor(具體實現(xiàn)是InstantiationAwareBeanPostProcessor). postProcessBeforeInstantiation();

這個階段允許在Bena進行實例化之前,允許開發(fā)者自定義邏輯,如返回一個代理對象。不過需要注意的是假如在這個階段返回了一個不為null的實例,spring就會中斷后續(xù)的過程。
BeanPostProcessor.postProcessAfterInstantiation();

這個階段是Bean實例化完畢后執(zhí)行的后處理操作,所有在初始化邏輯、裝配邏輯之前執(zhí)行

3.2:初始化階段

3.2.1:BeanPostProcessor.postProcessBeforeInitialization

該方法在bean初始化方法前被調用,Spring AOP的底層處理也是通過實現(xiàn)BeanPostProcessor來執(zhí)行代理邏輯的

3.2.2:InitializingBean.afterPropertiesSet

自定義屬性值該方法允許我們進行對對象中的屬性進行設置,假如在某些業(yè)務中,一個對象的某些屬性為null,但是不能顯示為null,比如顯示0或者其他的固定數值,我們就可以在這個方法實現(xiàn)中將null值轉換為特定的值

3.2.3:BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)??梢栽谶@個方法中進行bean的實例化之后的處理,比如我們的自定義注解,對依賴對象的版本控制自動路由切換。比如有一個服務依賴了兩種版本的實現(xiàn),我們如何實現(xiàn)自動切換呢?這時候可以自定義一個路由注解,假如叫@RouteAnnotaion,然后實現(xiàn)BeanPostProcessor接口,在其中通過反射拿到自定義的注解@RouteAnnotaion再進行路由規(guī)則的設定。


3.2.4:SmartInitializingSingleton.afterSingletonsInstantiated

4.1:容器啟動運行階段

4.1.1:SmartLifecycle.start

容器正式渲染完畢,開始啟動階段,bean已經在spring容器的管理下,程序可以隨時調用

5.1:容器停止銷毀

5.1.1:SmartLifecycle.stop(Runnable callback)

spring容器停止運行

5.1.2:DisposableBean.destroy()

spring會將所有的bean銷毀,實現(xiàn)的bean實例被銷毀的時候釋放資源被調用

四:一些關鍵性的問題

4.1:FactoryBean和BeanFactory的區(qū)別?

BeanFactory是個bean 工廠類接口,是負責生產和管理bean的工廠,是IOC容器最底層和基礎的接口,spring用它來管理和裝配普通bean的IOC容器,它有多種實現(xiàn),比如AnnotationConfigApplicationContext、XmlWebApplicationContext等。

FactoryBean是FactoryBean屬于spring的一個bean,在IOC容器的基礎上給Bean的實現(xiàn)加上了一個簡單工廠模式和裝飾模式,是一個可以生產對象和裝飾對象的工廠bean,由spring管理,生產的對象是由getObject()方法決定的。注意:它是泛型的,只能固定生產某一類對象,而不像BeanFactory那樣可以生產多種類型的Bean。在對于某些特殊的Bean的處理中,比如Bean本身就是一個工廠,那么在其進行單獨的實例化操作邏輯中,可能我們并不想走spring的那一套邏輯,此時就可以實現(xiàn)FactoryBean接口自己控制邏輯。

4.2:spring如何解決循環(huán)依賴問題

循環(huán)依賴問題就是A->B->A,spring在創(chuàng)建A的時候,發(fā)現(xiàn)需要依賴B,因為去創(chuàng)建B實例,發(fā)現(xiàn)B又依賴于A,又去創(chuàng)建A,因為形成一個閉環(huán),無法停止下來就可能會導致cpu計算飆升

如何解決這個問題呢?spring解決這個問題主要靠巧妙的三層緩存,所謂的緩存主要是指這三個map,singletonObjects主要存放的是單例對象,屬于第一級緩存;singletonFactories屬于單例工廠對象,屬于第三級緩存;earlySingletonObjects屬于第二級緩存,如何理解early這個標識呢?它表示只是經過了實例化尚未初始化的對象。Spring首先從singletonObjects(一級緩存)中嘗試獲取,如果獲取不到并且對象在創(chuàng)建中,則嘗試從earlySingletonObjects(二級緩存)中獲取,如果還是獲取不到并且允許從singletonFactories通過getObject獲取,則通過singletonFactory.getObject()(三級緩存)獲取。如果獲取到了則移除對應的singletonFactory,將singletonObject放入到earlySingletonObjects,其實就是將三級緩存提升到二級緩存,這個就是緩存升級。spring在進行對象創(chuàng)建的時候,會依次從一級、二級、三級緩存中尋找對象,如果找到直接返回。由于是初次創(chuàng)建,只能從第三級緩存中找到(實例化階段放入進去的),創(chuàng)建完實例,然后將緩存放到第一級緩存中。下次循環(huán)依賴的再直接從一級緩存中就可以拿到實例對象了。

五:測試

我們來寫一個測試類,驗證一下上面的問題:

5.1:首先聲明一個自定義的Bean

@Component
public class CustomBean {
    public CustomBean(){
        System.out.println("調用CustomBean空的構造方法");
    }
}

5.2:聲明一個Bean來實現(xiàn)BeanPostProcessor

package com.wyq.spring.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.beans.PropertyDescriptor;

/**
 * @Author: wyq
 * @Desc:
 * @Date: 2019/9/1 15:36
 **/
@Component
@Scope("singleton")
public class TestBean implements BeanPostProcessor, SmartInitializingSingleton, InstantiationAwareBeanPostProcessor, DisposableBean{

    private static final String BEAN_NAME= "customBean";

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (BEAN_NAME.equals(beanName)) {
            System.out.println("==>BeanPostProcessor.postProcessBeforeInitialization");
        }
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (BEAN_NAME.equals(beanName)) {
            System.out.println("==>BeanPostProcessor.postProcessAfterInitialization");
        }
        return null;
    }


    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("==>SmartInitializingSingleton.afterSingletonsInstantiated");

    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (BEAN_NAME.equals(beanName)) {
            System.out.println("==>InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation");
        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (BEAN_NAME.equals(beanName)) {
            System.out.println("==>InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation");
        }
        return false;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        System.out.println("==>InstantiationAwareBeanPostProcessor.postProcessPropertyValues");
        return null;
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("==>DisposableBean.destroy");
    }
}

5.3:啟動容器:

六:總結

本篇博客主要是介紹了Spring的一些實例化的過程,高屋建瓴的分析了一下spring的bean加載過程,沒有詳細展開某個細節(jié)分析。spring的內部源碼非常復雜,每個接口的實現(xiàn)類都在5個以上,如果深入細節(jié),恐怕不是一篇博客能講清楚的。這篇博客的目的就是在闡述spring的基本脈絡中心路線順序,首先我們需要有一個總體的認識,然后再深入到細節(jié)就是輕而易舉的了。這也是一種學習的方法論,通過本篇博客我希望能梳理清楚spring的基本流程,對spring有一個比較清晰的認識。并且學習到優(yōu)秀開源框架的設計基本思想,還有就是進一步提升自己的閱讀源碼的能力。

到此這篇關于解析spring加載bean流程的方法的文章就介紹到這了,更多相關spring加載bean內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • SpringBoot中獲取profile的方法詳解

    SpringBoot中獲取profile的方法詳解

    這篇文章主要介紹了springboot獲取profile的操作,文中的示例代碼講解詳細,具有很好的參考價值,希望對大家有所幫助
    2022-04-04
  • 基于SpringBoot解決CORS跨域的問題(@CrossOrigin)

    基于SpringBoot解決CORS跨域的問題(@CrossOrigin)

    這篇文章主要介紹了基于SpringBoot解決CORS跨域的問題(@CrossOrigin),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • 關于kafka消費不到遠程bootstrap-server?數據的問題

    關于kafka消費不到遠程bootstrap-server?數據的問題

    很多朋友遇到kafka消費不到遠程bootstrap-server?數據的問題,怎么解決這個問題,很多朋友不知所措,下面小編給大家?guī)砹岁P于kafka消費不到遠程bootstrap-server?數據的問題及解決方法,感興趣的朋友跟隨小編一起看看吧
    2021-11-11
  • System.currentTimeMillis()計算方式與時間的單位轉換詳解

    System.currentTimeMillis()計算方式與時間的單位轉換詳解

    這篇文章主要介紹了System.currentTimeMillis()計算方式與時間的單位轉換詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • java中transient關鍵字分析

    java中transient關鍵字分析

    這篇文章主要介紹了java中transient關鍵字分析,transient與類對象的序列化息息相關,序列化保存的是 類對象 狀態(tài),被transient關鍵字修飾的成員變量,在類的實例化對象的序列化處理過程中會被忽略,變量不會貫穿對象的序列化和反序列化,需要的朋友可以參考下
    2023-09-09
  • java實現(xiàn)簡單發(fā)送郵件功能

    java實現(xiàn)簡單發(fā)送郵件功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)簡單發(fā)送郵件功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Spring Cloud中FeignClient實現(xiàn)文件上傳功能

    Spring Cloud中FeignClient實現(xiàn)文件上傳功能

    這篇文章主要為大家詳細介紹了Spring Cloud中FeignClient實現(xiàn)文件上傳功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • 十分簡單易懂的Java應用程序性能調優(yōu)技巧分享

    十分簡單易懂的Java應用程序性能調優(yōu)技巧分享

    這篇文章主要介紹了十分簡單易懂的Java性能調優(yōu)技巧分享,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • JAVA自定義異常使用方法實例詳解

    JAVA自定義異常使用方法實例詳解

    這篇文章主要介紹了JAVA自定義異常使用方法實例詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • Java實現(xiàn)將圖片上傳到webapp路徑下 路徑獲取方式

    Java實現(xiàn)將圖片上傳到webapp路徑下 路徑獲取方式

    這篇文章主要介紹了Java實現(xiàn)將圖片上傳到webapp路徑下 路徑獲取方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論