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

Spring底層原理深入分析

 更新時(shí)間:2022年07月11日 14:14:00   作者:PnJg?  
Spring框架是一個(gè)開放源代碼的J2EE應(yīng)用程序框架,由Rod Johnson發(fā)起,是針對(duì)bean的生命周期進(jìn)行管理的輕量級(jí)容器(lightweight container)。 Spring解決了開發(fā)者在J2EE開發(fā)中遇到的許多常見的問題,提供了功能強(qiáng)大IOC、AOP及Web MVC等功能

bean生命周期

userService.class--->推斷構(gòu)造方法--->對(duì)象--->依賴注入--->初始化前(@postConstruct)--->初始化(@afterPropertiesSet)--->初始化后(AOP)--->放入Map(單例池)--->Bean對(duì)象

推斷構(gòu)造方法的底層原理

1、使用哪個(gè)構(gòu)造方法

@Component
public class OrderService {
    private UserService userService;
    @Override
    public OrderService (UserService userService) {
       this.userService =  userService;
    }
    @Override
    public void test) {
        System.out.println(userService);
    }
}

在上述例子中,因?yàn)閷懥艘粋€(gè)有參構(gòu)造方法,所以無參構(gòu)造方法不能用了。

這個(gè)時(shí)候在userService屬性上面沒有加@Autowired注解,但是打印發(fā)現(xiàn)這個(gè)userService對(duì)象存在。

orderService是一個(gè)bean,spring想去創(chuàng)造這個(gè)bean,就要去用構(gòu)造方法,發(fā)現(xiàn)構(gòu)造方法是有參的,就回去找一個(gè)userService對(duì)象賦給這個(gè)屬性。

當(dāng)加上無參構(gòu)造方法之后,spring就會(huì)去用無參的構(gòu)造方法,這時(shí)候userService沒有值;當(dāng)有多個(gè)構(gòu)造方法的時(shí)候,沒有明確告知的情況下(告知是用@Autowired注解),spring會(huì)去找無參的構(gòu)造方法,如果沒有無參構(gòu)造方法就直接報(bào)錯(cuò)。

對(duì)于第一種情況:“orderService是一個(gè)bean,spring想去創(chuàng)造這個(gè)bean,就要去用構(gòu)造方法,發(fā)現(xiàn)構(gòu)造方法是有參的,就回去找一個(gè)userService對(duì)象賦給這個(gè)屬性。”的userService對(duì)象spring是從哪里找出來賦值給屬性的呢?

spring首先會(huì)去單例池根據(jù)beanName即userService找有沒有相應(yīng)的bean對(duì)象,如果有就直接賦值給屬性。如果沒有,就去創(chuàng)建(前提是orderService是一個(gè)bean,但是沒來得及創(chuàng)建,但如果是多例bean就直接去創(chuàng)建)。

但是如果是去創(chuàng)建的話就有可能出現(xiàn)循環(huán)依賴,考慮在userService中有orderService屬性,并有一個(gè)有參構(gòu)造方法:

@Component
public class UserService{
    private OrderService orderService ;
    @Override
    public UserService(OrderService orderService ) {
       this.orderService =  orderService ;
    }
    @Override
    public void test) {
        System.out.println(orderService );
    }
}

這時(shí)候在創(chuàng)建orderService時(shí)需要用到有參構(gòu)造方法,因?yàn)闆]有userService,這時(shí)候就要去創(chuàng)建,創(chuàng)建userService就要用到構(gòu)造方法,可是完蛋,這是又需要orderService,可是orderService本身就在創(chuàng)建,也就是發(fā)生了循環(huán)依賴。

2、如果有參把哪個(gè)bean對(duì)象賦值給入?yún)?/h3>

假設(shè)單例池中有userService對(duì)象,可以直接拿出來用,但是怎么在單例池中拿到這個(gè)bean呢?因?yàn)閰?shù)名是可以隨意設(shè)定的,所以不能直接拿參數(shù)名去找,所以要根據(jù)類型去單例池找,如果只有一個(gè)該類型的bean對(duì)象,直接賦值。但是有可能在單例池中存在多個(gè)同類型的對(duì)象(不同的beanName),這時(shí)候再根據(jù)參數(shù)名去匹配,如果找到了就直接賦值,匹配不上就報(bào)錯(cuò)。(先byType,再byName)

AOP實(shí)現(xiàn)原理

開啟AOP動(dòng)態(tài)代理之后,原本例子中的userService沒有值了,因?yàn)锳OP是發(fā)生在初始化之后,而初始化之后拿到的動(dòng)態(tài)代理對(duì)象是不會(huì)去再去做依賴注入,直接放入了單例池,所以即使屬性上面有@Autowired注解也沒用。

cglib是基于父子類實(shí)現(xiàn)的,代理對(duì)象實(shí)質(zhì)是繼承了普通對(duì)象,并且代理對(duì)象中會(huì)有一個(gè)普通對(duì)象的屬性、以及被增強(qiáng)的方法,在被增強(qiáng)方法中會(huì)先執(zhí)行切面邏輯,再執(zhí)行普通對(duì)象的方法,而普通對(duì)象中是有值的

spring事務(wù)

根據(jù)上面的AOP實(shí)現(xiàn),事務(wù)是基于AOP的實(shí)現(xiàn),生成的是代理類,如果有@Transactional就開啟spring事務(wù)切面:

1、事務(wù)管理器會(huì)新建數(shù)據(jù)庫連接,并且設(shè)置conn.autocommit = false,因?yàn)椴还苁莔ybatis還是jdbctemplate都是自動(dòng)提交,這樣就算出現(xiàn)異常,也已經(jīng)提交了。在新建之后,當(dāng)target即普通對(duì)象去執(zhí)行test方法市,不管是mybatis還是jdbctemplate操作數(shù)據(jù)庫都要拿到這個(gè)連接才能執(zhí)行sql

2、如果執(zhí)行完沒有拋異常就執(zhí)行conn.commit

3、

在a方法上的注解加了never,原本應(yīng)該是要拋出異常的,但是還是順利寫進(jìn)了數(shù)據(jù)庫,原因是執(zhí)行a方法的還是userService的普通對(duì)象(沒有經(jīng)過AOP增強(qiáng)的對(duì)象),就識(shí)別不了注解。為什么第一個(gè)test方法可以識(shí)別?因?yàn)橐婚_始是被spring管理的bean對(duì)象userService執(zhí)行,會(huì)有相應(yīng)的邏輯代碼去識(shí)別注解,識(shí)別到注解后生成了代理類和代理對(duì)象,然后去運(yùn)行的test方法,但是執(zhí)行a方法的時(shí)候相當(dāng)于是 new userService,沒有對(duì)應(yīng)的邏輯代碼去識(shí)別注解。

解決辦法:把userService拆出一個(gè)新的類,把a(bǔ)方法寫進(jìn)新類

@Configuration

一開始沒有加@configuration注解回滾失敗。

jdbcTemplate是拿事務(wù)管理器新建的數(shù)據(jù)庫連接conn。jdbcTemplate是通過ThreadLocal<Map<DataSource, conn>,線程可能會(huì)執(zhí)行很多方法,可能會(huì)有執(zhí)行不同的datasource,所以是一個(gè)map。

因?yàn)檎Z法邏輯中jdbcTemplate和事務(wù)管理器中是返回新new出來的datasource對(duì)象,這樣如果沒有@configuration,那么jdbcTemplate和事務(wù)管理器拿到的是兩個(gè)不同的datasource對(duì)象,那么jdbcTemplate去Map里面找不到對(duì)應(yīng)的conn,只能自己創(chuàng)建新的連接,這樣就不能被spring事務(wù)管理。

而如果加上了@configuration,那么AppConfig會(huì)基于動(dòng)態(tài)代理產(chǎn)生AppConfig代理對(duì)象

AppConfig代理對(duì)象會(huì)先執(zhí)行自己的代理邏輯,然后去執(zhí)行普通對(duì)象的jdbcTemplate方法,進(jìn)到父類的jdbcTemplate方法后會(huì)執(zhí)行dataSource方法,但是都是代理對(duì)象在執(zhí)行。代理對(duì)象執(zhí)行dataSource方法的時(shí)候先執(zhí)行代理邏輯:先去spring容器有沒有dataSource這個(gè)bean,如果沒有就創(chuàng)建,如果有就直接返回。

那么就能拿到一樣的datasource對(duì)象。

循環(huán)依賴

為什么會(huì)出現(xiàn)循環(huán)依賴

首先上面這個(gè)例子考慮打破循環(huán)依賴。

可以添加一個(gè)map<"對(duì)象名",對(duì)象>,并把實(shí)例化AService得到的普通對(duì)象放入這個(gè)map中,這樣在B填充A屬性的時(shí)候就把AService普通對(duì)象注入,B就可以完成創(chuàng)建并放入了單例池,A也就能把單例池中的B對(duì)象注入。

但是存在的問題是如果AService在初始化后需要進(jìn)行AOP,那么最終放入單例池里面的會(huì)是AService的代理對(duì)象,但是BService拿到的是AService普通對(duì)象,因?yàn)锳Service是單例bean,所以只能有一個(gè)對(duì)象在單例池中,又因?yàn)檫M(jìn)行了AOP,所以只能是AService的代理對(duì)象,并且在其他地方如果依賴了AService,那么應(yīng)該拿到的是AService的代理對(duì)象。

提前AOP

解決方法上述打破循環(huán)依賴出現(xiàn)的問題的方法是在把AOP提前,讓B創(chuàng)建注入A屬性的時(shí)候拿到的是AService的代理對(duì)象,即提前AOP。如果出現(xiàn)了循環(huán)依賴,那么就提前AOP,否則還是在初始化后進(jìn)行AOP。

如何判斷出現(xiàn)了循環(huán)依賴?創(chuàng)建一個(gè)creatingSet<beanName>,放入正在創(chuàng)建的bean的名稱,代表該bean正在創(chuàng)建,后續(xù)可以在屬性注入的步驟中,如果在單例池中找不到對(duì)應(yīng)的bean對(duì)象而在creatingSet中找到了,就可以判定出現(xiàn)了循環(huán)依賴。

在判定出現(xiàn)循環(huán)依賴之后進(jìn)行了提前AOP,那么應(yīng)該什么時(shí)候創(chuàng)建AService的代理對(duì)象使得BService注入屬性的時(shí)候拿到的是AService的代理并放入單例池呢?跳到二級(jí)緩存

第一級(jí)緩存singletonObjects

即單例池

第二級(jí)緩存earlySingletonObjects

二級(jí)緩存的作用是為了保證單例性:用于出現(xiàn)循環(huán)依賴的情況下,會(huì)提前產(chǎn)生一個(gè)沒有經(jīng)過完整生命周期的早期bean對(duì)象,并保存在二級(jí)緩存中。否則可能多次創(chuàng)建同一個(gè)類型的bean對(duì)象。

考慮如下例子:在上述例子中AService再加入一個(gè)CService屬性,并且在CService也依賴AService屬性

在進(jìn)行bService的生命周期注入aService時(shí)會(huì)先去二級(jí)緩存中根據(jù)beanName找有沒有aService的bean對(duì)象,如果沒有就進(jìn)行AOP并創(chuàng)建aService的代理對(duì)象放入二級(jí)緩存。

當(dāng)進(jìn)行cService的生命周期注入aService時(shí)就去二級(jí)緩存中找,發(fā)現(xiàn)已經(jīng)有了aService,只可以直接取得

但是二級(jí)緩存中存放的不是完整的生命周期的bean對(duì)象,所以完成屬性填充等動(dòng)作之后從二級(jí)緩存中拿到aService的代理對(duì)象放入單例池中。

這時(shí)候就不需要在第四步進(jìn)行AOP了,并且因?yàn)锳OP的實(shí)質(zhì)是在原有bean的基礎(chǔ)上加入切面邏輯,并且AOP后生成的代理對(duì)象中還是會(huì)有target普通對(duì)象

所以在進(jìn)行屬性填充等動(dòng)作的時(shí)候還是對(duì)AService的普通對(duì)象進(jìn)行的,那么代理的對(duì)象中的普通對(duì)象還是可以拿到這些屬性值,就相當(dāng)于代理對(duì)象也拿到了

第三級(jí)緩存singletonFactories

打破循環(huán)依賴的關(guān)鍵,類似于上面提及的map,只是在spring的實(shí)現(xiàn)中key值是beanName,value是一個(gè)lamda表達(dá)式,三級(jí)緩存中不去判斷是否出現(xiàn)循環(huán)依賴,而是只要是支持循環(huán)依賴并且是單例的,那么就會(huì)加入三級(jí)緩存

而lamda表達(dá)式返回的是一個(gè)對(duì)象,執(zhí)行l(wèi)amda方法的時(shí)候就執(zhí)行了aop,所以返回的是一個(gè)代理對(duì)象

在底層源碼中,通過第三級(jí)緩存來控制第四步中是否需要AOP

如果第三級(jí)緩存的map中remove出來是null,整明沒有循壞依賴,就這時(shí)候進(jìn)行AOP并返回增強(qiáng)后的代理對(duì)象,反之整明之前已經(jīng)進(jìn)行了AOP,不需要再進(jìn)行AOP,直接返回普通對(duì)象。

到此這篇關(guān)于Spring底層原理深入分析的文章就介紹到這了,更多相關(guān)Spring底層原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解析整合mybatis-spring需要的maven依賴配置問題

    解析整合mybatis-spring需要的maven依賴配置問題

    這篇文章主要介紹了整合mybatis-spring需要的maven依賴配置問題,創(chuàng)建Maven項(xiàng)目,導(dǎo)入相關(guān)jar包,文中還給大家提到了,解決maven靜態(tài)資源約定大于習(xí)慣問題,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11
  • Java高效數(shù)據(jù)傳輸通過綁定快速將數(shù)據(jù)導(dǎo)出至Excel

    Java高效數(shù)據(jù)傳輸通過綁定快速將數(shù)據(jù)導(dǎo)出至Excel

    這篇文章主要介紹了Java高效數(shù)據(jù)傳輸之通過綁定快速將數(shù)據(jù)導(dǎo)出至Excel方法實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • JAVA中使用FileWriter寫數(shù)據(jù)到文本文件步驟詳解

    JAVA中使用FileWriter寫數(shù)據(jù)到文本文件步驟詳解

    這篇文章主要介紹了JAVA中使用FileWriter寫數(shù)據(jù)到文本文件步驟詳解,FileWriter類提供了多種寫入字符的方法,包括寫入單個(gè)字符、寫入字符數(shù)組和寫入字符串等,它還提供了一些其他的方法,如刷新緩沖區(qū)、關(guān)閉文件等,需要的朋友可以參考下
    2023-10-10
  • SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis)

    SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis)

    這篇文章主要介紹了SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • java泛型類的定義與使用詳解

    java泛型類的定義與使用詳解

    這篇文章主要為大家詳細(xì)介紹了java泛型類定義與使用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 最新評(píng)論