spring中Bean的創(chuàng)建流程

spring中的Bean是如何創(chuàng)建的?

帶著這個(gè)問題,從一個(gè)簡(jiǎn)單的例子開始一探究竟。

  public class TestSpring {

    public static void main(String[] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

這是一個(gè)基于JavaConfig方式來創(chuàng)建spring容器,spring容器創(chuàng)建完之后就開始嘗試獲取UserService對(duì)象,接著調(diào)用userService的test方法,很簡(jiǎn)單的一個(gè)例子。
先貼出UserService的代碼:

@Service
@LogAround(bizTag = "UserService")
public class UserService implements BeanNameAware,InitializingBean {

    @Autowired
    private OrderService orderService;

    private User adminUser;

    private String beanName;

    /**
     * 構(gòu)造函數(shù)
     */
    public UserService(){
        System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)");
    }

    /**
     * BeanNameAware自動(dòng)給UserService設(shè)置beanName
     * @param beanName
     */
    @Override
    public void setBeanName(String beanName) {
        System.out.println("aware自動(dòng)設(shè)值-BeanNameAware");
        this.beanName = beanName;
    }

    /**
     * 初始化前
     */
    @PostConstruct
    public void loadAdminUser(){
        System.out.println("初始化前-PostConstruct");
        User user = new User();
        user.setId(1L);
        user.setName("admin");
        this.adminUser = user;
    }

    /**
     * 初始化
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化-afterPropertiesSet");
    }

    public String getBeanName(){
        System.out.println(beanName);
        return beanName;
    }

    public User getAdminUser() {
        return adminUser;
    }
    public void test(){
        System.out.println("test");
    }
}

該如何入手分析UserService這個(gè)Bean是如何創(chuàng)建的呢?
第一步,先想下spring幫我們創(chuàng)建的Bean和我們自己在程序中new UserService()有什么區(qū)別?可能區(qū)別有很多,比如spring幫我們創(chuàng)建的bean是由spring管理的,默認(rèn)是單例的等。但是不管怎么樣spring要想創(chuàng)建Bean,第一步肯定是要拿到構(gòu)造函數(shù)通過反射的方式在Spring容器中先new出這么一個(gè)對(duì)象,我們暫且叫它普通對(duì)象,此時(shí)和我們自己在程序中new UserService()并沒有什么區(qū)別,就是創(chuàng)建一個(gè)普通的對(duì)象。
那第二歩會(huì)做什么呢?此時(shí)這個(gè)普通對(duì)象里面的屬性是沒有值的。bean和new出的對(duì)象還有一個(gè)區(qū)別就是spring會(huì)幫我們的bean中的屬性做注入(依賴注入),這樣bean中的屬性才會(huì)有值,第二歩就是會(huì)對(duì)@Autowired注解修飾的屬性進(jìn)行依賴入。
依賴注入之后,還會(huì)aware接口設(shè)值,有初始化前,初始化和初始化后的動(dòng)作,這些步驟都做完之后才會(huì)生成一個(gè)真正的Bean而不是普通的對(duì)象。
實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),run一下上面的main方法,先看下bean創(chuàng)建的主要步驟后在逐個(gè)解析。


控制臺(tái)輸出

如上圖:

  1. 創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)
  2. 對(duì)@Autowired注解的屬性進(jìn)行賦值(依賴注入)
  3. aware自動(dòng)設(shè)值-BeanNameAware
  4. 初始化前-PostConstruct
  5. 初始化-afterPropertiesSet
  6. 其實(shí)還有一個(gè)初始后的動(dòng)作

Bean的創(chuàng)建流程

通過上面的分析和代碼的運(yùn)行結(jié)果可以大致的看出spring中bean的創(chuàng)建有以下幾個(gè)主要流程:


創(chuàng)建流程
  1. 利用該類的構(gòu)造方法來實(shí)例化得到一個(gè)對(duì)象(但是如果一個(gè)類中有多個(gè)構(gòu)造方法, Spring則會(huì)進(jìn)行選擇,這個(gè)叫做推斷構(gòu)造方法,下文在詳細(xì)介紹)
  2. 得到一個(gè)對(duì)象后,Spring會(huì)判斷該對(duì)象中是否存在被@Autowired注解了的屬 性,把這些屬性找出來并由Spring進(jìn)行賦值(依賴注入)
  3. 依賴注入后,Spring會(huì)判斷該對(duì)象是否實(shí)現(xiàn)了BeanNameAware接口、 BeanClassLoaderAware接口、BeanFactoryAware接口,如果實(shí)現(xiàn)了,就表示當(dāng)前 對(duì)象必須實(shí)現(xiàn)該接口中所定義的setBeanName()、setBeanClassLoader()、 setBeanFactory()方法,那Spring就會(huì)調(diào)用這些方法并傳入相應(yīng)的參數(shù)
  4. Aware回調(diào)后,Spring會(huì)判斷該對(duì)象中是否存在某個(gè)方法被@PostConstruct注解 了,如果存在,Spring會(huì)調(diào)用當(dāng)前對(duì)象的此方法(初始化前),上面的代碼中初始化了一個(gè)adminUser的數(shù)據(jù)。
  5. 緊接著,Spring會(huì)判斷該對(duì)象是否實(shí)現(xiàn)了InitializingBean接口,如果實(shí)現(xiàn)了,就 表示當(dāng)前對(duì)象必須實(shí)現(xiàn)該接口中的afterPropertiesSet()方法,那Spring就會(huì)調(diào)用當(dāng)
    前對(duì)象中的afterPropertiesSet()方法(初始化)
  6. 最后,Spring會(huì)判斷當(dāng)前對(duì)象需不需要進(jìn)行AOP,如果不需要那么Bean就創(chuàng)建完 了,如果需要進(jìn)行AOP,則會(huì)進(jìn)行動(dòng)態(tài)代理并生成一個(gè)代理對(duì)象做為Bean(初始化 后)
    關(guān)于第6步控制臺(tái)沒辦法打出日志,因?yàn)槌跏己笊婕暗絪pring的源碼操作,但是可以通過斷點(diǎn)看一下,提一句例子中UserService是被LogAspect切面切的。


    AOP最終生成代理對(duì)象

    如果UserService類@LogAround(bizTag = "UserService")注解去掉,就說明UserService類不需要AOP,那最終生成的Bean就是構(gòu)造函數(shù)所得到的對(duì)象。
    有AOP的話那么Bean就是UserService的代理類所實(shí)例化得到的對(duì)象。

這樣,一個(gè)Bean就創(chuàng)建完了,創(chuàng)建完之后呢?
如果當(dāng)前Bean是單例Bean,那么會(huì)把該Bean對(duì)象存入一個(gè)Map<String, Object>,Map的key為beanName,value為Bean對(duì)象。這樣下次getBean時(shí)就可 以直接從Map中拿到對(duì)應(yīng)的Bean對(duì)象了。(實(shí)際上,在Spring源碼中,這個(gè)Map就 是單例池)
如果當(dāng)前Bean是原型Bean,那么后續(xù)沒有其他動(dòng)作,不會(huì)存入一個(gè)Map,下次 getBean時(shí)會(huì)再次執(zhí)行上述創(chuàng)建過程,得到一個(gè)新的Bean對(duì)象。

推斷構(gòu)造方法

至此,我們清楚了Bean的創(chuàng)建流程,那如果UserService中有多個(gè)構(gòu)造函數(shù)呢?第一步還能順利的創(chuàng)建一個(gè)普通對(duì)象嗎?這里面涉及到一個(gè)概念推斷構(gòu)造方法,就是spring會(huì)去推斷用哪個(gè)構(gòu)造方法來創(chuàng)建出普通對(duì)象。
做幾個(gè)小實(shí)驗(yàn):

public UserService(){
        System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)");
    }
    public UserService(OrderService orderService){
        System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService參數(shù)");
    }
    public UserService(OrderService orderService,String beanName){
        System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService和beanName參數(shù)");
    }

初始有這三個(gè)構(gòu)造方法,一個(gè)是無參構(gòu)造方法,一個(gè)是只有一個(gè)參數(shù),一個(gè)是有兩個(gè)參數(shù)。
場(chǎng)景1:和初始情況一樣,Bean能夠正常創(chuàng)建,并且打印"創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)"
場(chǎng)景2:注釋掉第1個(gè)構(gòu)造方法,創(chuàng)建Bean的時(shí)候就報(bào)異常了No default constructor found具體如下
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [D:\work\project\test\target\classes\com\test\service\UserService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.test.service.UserService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.test.service.UserService.<init>()
場(chǎng)景3:注釋掉第1個(gè)和第3個(gè)構(gòu)造方法,能創(chuàng)建成功并打印"創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService參數(shù)"
場(chǎng)景4:注釋掉第1個(gè)和第2個(gè)構(gòu)造方法,創(chuàng)建失敗,第二個(gè)參數(shù)String類型沒地方拿到值。
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in file [D:\work\project\test\target\classes\com\test\service\UserService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
以上幾個(gè)場(chǎng)景總結(jié)下:

  1. 如果一個(gè)類只有一個(gè)構(gòu)造方法,那么沒得選擇,只能用這個(gè)構(gòu)造方法。但是有參的構(gòu)造方法,參數(shù)必須是spring的Bean這樣spring才能拿到進(jìn)行賦值。
  2. 如果一個(gè)類存在多個(gè)構(gòu)造方法,Spring不知道如何選擇,就會(huì)看是否有無參的構(gòu) 造方法,因?yàn)闊o參構(gòu)造方法本身表示了一種默認(rèn)的構(gòu)造方法。
  3. 如果都沒有構(gòu)造方法,就是用默認(rèn)的無參構(gòu)造方法來創(chuàng)建。
    其實(shí)多個(gè)構(gòu)造函數(shù),也可以手動(dòng)指定告訴spring用哪個(gè)構(gòu)造函數(shù)來創(chuàng)建,那就是加了@Autowired注解


    指定構(gòu)造方法

    我們通常說的依賴注入,
    屬性注入
    @Autowired
    private OrderService orderService;
    這邊的構(gòu)造方法
    public UserService(OrderService orderService){
    System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService參數(shù)");
    }
    就是構(gòu)造函數(shù)注入

依賴注入流程

不管是屬性注入還是構(gòu)造方法注入,能提供的信息只有兩個(gè)一個(gè)是類型OrderService ,一個(gè)是名字orderService。那到底是根據(jù)類型注入的還是根據(jù)名字注入的呢?
假設(shè)根據(jù)名字注入的那剛好有一個(gè)其他類型的Bean名字也叫orderService那注入的時(shí)候豈不是會(huì)類型不匹配異常。比如說剛好有一個(gè)OrderBaseService類但是beanName也叫orderService,如果根據(jù)名字注入的話拿到的是OrderBaseService對(duì)象顯然類型不匹配。所以注入通常是先根據(jù)類型來查找的:

  1. 先根據(jù)入?yún)㈩愋驼?,如果只找到一個(gè)不用管name,那就直接用來作為入?yún)?/li>
  2. 如果根據(jù)類型找到多個(gè),則再根據(jù)入?yún)⒚謥泶_定唯一
  3. 最終如果沒有找到,則會(huì)報(bào)錯(cuò),無法創(chuàng)建當(dāng)前Bean對(duì)象

代理對(duì)象生成

代理對(duì)象通常是AOP的時(shí)候會(huì)生成代理對(duì)象還有一種就是開啟事務(wù)的時(shí)候也會(huì)生成代理對(duì)象。否則的話Bean都是直接根據(jù)構(gòu)造函數(shù)生成對(duì)象在進(jìn)行依賴注入和初始化等流程。

AOP代理對(duì)象生成

AOP就是進(jìn)行動(dòng)態(tài)代理,在創(chuàng)建一個(gè)Bean的過程中,Spring在最后一步會(huì)去判斷當(dāng)前正在 創(chuàng)建的這個(gè)Bean是不是需要進(jìn)行AOP,如果需要?jiǎng)t會(huì)進(jìn)行動(dòng)態(tài)代理。

如何判斷當(dāng)前Bean對(duì)象需不需要進(jìn)行AOP:

  1. 找出所有的切面Bean
  2. 遍歷切面中的每個(gè)方法,看是否寫了@Before、@After等注解
  3. 如果寫了,則判斷所對(duì)應(yīng)的Pointcut是否和當(dāng)前Bean對(duì)象的類是否匹配
  4. 如果匹配則表示當(dāng)前Bean對(duì)象有匹配的的Pointcut,表示需要進(jìn)行AOP

利用cglib進(jìn)行AOP的大致流程:

  1. 生成代理類UserServiceProxy,代理類繼承UserService
  2. 代理類中重寫了父類的方法,比如UserService中的test()方法
  3. 代理類中還會(huì)有一個(gè)target屬性,該屬性的值為被代理對(duì)象(也就是通過 UserService類推斷構(gòu)造方法實(shí)例化出來的對(duì)象,進(jìn)行了依賴注入、初始化等步驟的 對(duì)象)
  4. 代理類中的test()方法被執(zhí)行時(shí)的邏輯如下:
    a. 執(zhí)行切面邏輯(@Before)
    b. 調(diào)用target.test()
    當(dāng)我們從Spring容器得到UserService的Bean對(duì)象時(shí),拿到的就是UserServiceProxy所生 成的對(duì)象,也就是代理對(duì)象。
    UserService代理對(duì)象.test()--->執(zhí)行切面邏輯--->target.test(),注意target對(duì)象不是代理 對(duì)象,而是被代理對(duì)象。

事務(wù)代理對(duì)象生成

Spring事務(wù) 當(dāng)我們?cè)谀硞€(gè)方法上加了@Transactional注解后,就表示該方法在調(diào)用時(shí)會(huì)開啟Spring事 務(wù),而這個(gè)方法所在的類所對(duì)應(yīng)的Bean對(duì)象會(huì)是該類的代理對(duì)象。
Spring事務(wù)的代理對(duì)象執(zhí)行某個(gè)方法時(shí)的步驟:

  1. 判斷當(dāng)前執(zhí)行的方法是否存在@Transactional注解
  2. 如果存在,則利用事務(wù)管理器(TransactionMananger)新建一個(gè)數(shù)據(jù)庫連接
  3. 修改數(shù)據(jù)庫連接的autocommit為false
  4. 執(zhí)行target.test(),執(zhí)行程序員所寫的業(yè)務(wù)邏輯代碼,也就是執(zhí)行sql
  5. 執(zhí)行完了之后如果沒有出現(xiàn)異常,則提交,否則回滾
    注意:Spring事務(wù)是否會(huì)失效的判斷標(biāo)準(zhǔn):某個(gè)加了@Transactional注解的方法被調(diào)用時(shí),要判 斷到底是不是直接被代理對(duì)象調(diào)用的,如果是則事務(wù)會(huì)生效,如果不是則失效。

總結(jié)

Spring中Bean的創(chuàng)建過程其實(shí)就是從一個(gè)普通對(duì)象蛻變成Bean的一個(gè)過程,蛻變包括依賴注入,初始化等步驟。最后在看下這個(gè)類是否有被AOP或開啟事務(wù)有的話會(huì)額外生成代理對(duì)象作為Bean。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容