<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手寫IOC容器-探究IOC的本質(zhì)原理

          共 6873字,需瀏覽 14分鐘

           ·

          2020-07-27 09:27

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)


          66套java從入門到精通實(shí)戰(zhàn)課程分享

          ? 作?|??hello-*-world

          來源 |? cnblogs.com/HTLucky/p/13379900.html


          IOC(控制翻轉(zhuǎn))是程序設(shè)計(jì)的一種思想,其本質(zhì)就是上端對(duì)象不能直接依賴于下端對(duì)象,要是依賴的話就要通過抽象來依賴。這是什么意思呢?意思就是上端對(duì)象如BLL層中,需要調(diào)用下端對(duì)象的DAL層時(shí)不能直接調(diào)用DAl的具體實(shí)現(xiàn),而是通過抽象的方式來進(jìn)行調(diào)用。這樣做是有一定的道理的。有這么一個(gè)場(chǎng)景,你們的項(xiàng)目本來是用Sqlserver來進(jìn)行數(shù)據(jù)訪問的,那么就會(huì)有一個(gè)SqlserverDal對(duì)象。BLL層調(diào)用的時(shí)候通過new SqlserverDal(),直接創(chuàng)建一個(gè)SqlserverDal對(duì)象進(jìn)行數(shù)據(jù)訪問,現(xiàn)在項(xiàng)目又要改為Mysql數(shù)據(jù)庫,用MysqlDal進(jìn)行數(shù)據(jù)訪問。這時(shí)候就麻煩了,你的BLL層將new SqlserverDal()全部改為new MysqlDal()。同理BLL層也是這個(gè)道理。這么做,從程序的架構(gòu)而言是相當(dāng)不合理的,我只是想將SqlserverDal替換為MysqlDal。按道理說我只要添加MysqlDal對(duì)象就可以了。可現(xiàn)在的做法是還要將BLL中的new SqlserverDal()全部改一遍。這未免有點(diǎn)得不償失了。這時(shí)IOC就排上用場(chǎng)了,IOC的核心理念就是上端對(duì)象通過抽象來依賴下端對(duì)象,那么我們?cè)贐LL中,不能直接通過new SqlserverDal()來創(chuàng)建一個(gè)對(duì)象,而是通過結(jié)構(gòu)來聲明(抽象的形式來進(jìn)行依賴),當(dāng)我們替換MysqlDal時(shí)我們只需讓MysqlDal也繼承這個(gè)接口,那么我們BLL層的邏輯就不用動(dòng)了。那么現(xiàn)在又有一個(gè)問題,對(duì)象我們可以用接口來接收,所有子類出現(xiàn)的地方都可以用父類來替代,這沒毛病。但對(duì)象的創(chuàng)建還是要知道具體的類型,還是通過之前的new SqlserverDal()這種方式創(chuàng)建對(duì)象。肯定是不合理的,這里我們還是依賴于細(xì)節(jié)。

          那我們需要怎么處理呢?這時(shí)候IOC容器就該上場(chǎng)了,IOC容器可以理解為一個(gè)第三方的類,專門為我們創(chuàng)建對(duì)象用的,它不需要關(guān)注具體的業(yè)務(wù)邏輯,也不關(guān)注具體的細(xì)節(jié)。你只需將你需要的創(chuàng)建的對(duì)象類型傳給它,它就能幫我們完成對(duì)象的創(chuàng)建。常見的IOC容器有Autofac,Unity

          接觸.net core的小伙伴可能對(duì)容器很熟悉,.net core中將IOC容器內(nèi)置了。創(chuàng)建對(duì)象需要先進(jìn)行注冊(cè)

          public?void?ConfigureServices(IServiceCollection services)
          ????????
          {
          ????????????services.AddTransient();
          ????????????services.AddTransient();

          ????????}


          從上面的示例我們可以看到.net core中是通過ServiceCollection容器幫我們完成對(duì)象的創(chuàng)建,我們只需將接口的類型和要?jiǎng)?chuàng)建對(duì)象的類型傳進(jìn)去,它就能幫我們完成對(duì)象的創(chuàng)建。那么它的原理是啥呢,我們能不能創(chuàng)建自已的容器來幫我們完成對(duì)象的創(chuàng)建呢,讓我們帶著疑惑繼續(xù)往下走

          一.容器雛形

          這里我們先不考慮那么多,我們先寫一個(gè)容器,幫我們完成對(duì)象的創(chuàng)建工作。


          public??class?HTContainer : IHTContainer
          ????{
          ????????//創(chuàng)建一個(gè)Dictionary數(shù)據(jù)類型的對(duì)象用來存儲(chǔ)注冊(cè)的對(duì)象
          ????????private?Dictionary<string, Type> TypeDictionary = new?Dictionary<string, Type>();
          ????????//注冊(cè)方法,用接口的FullName為key值,value為要?jiǎng)?chuàng)建對(duì)象的類型
          ????????public?void?RegisterType()
          ????????{
          ????????????this.TypeDictionary.Add(typeof(IT).FullName, typeof(T));
          ????????}

          ????????//創(chuàng)建對(duì)象通過傳遞的類型進(jìn)行匹配
          ????????public?IT Resolve()
          ????????{
          ????????????string?key = typeof(IT).FullName;
          ????????????Type type?= this.TypeDictionary[key]; //獲取要?jiǎng)?chuàng)建對(duì)象的類型
          ????????????//這里先不考慮有參構(gòu)造函數(shù)的問題,后面會(huì)逐一的解決這些問題
          ????????????return??(IT)Activator.CreateInstance(type); //通過反射完成對(duì)象的創(chuàng)建,這里我們先不考慮參數(shù)問題
          ????????????
          ????????}
          ????}


          簡單調(diào)用


          //實(shí)例化容器對(duì)象
          ????????????IHTContainer container = new?HTContainer();
          ????????????//注冊(cè)對(duì)象
          ????????????container.RegisterType();
          ????????????//通過容器完成對(duì)象的創(chuàng)建,不體現(xiàn)細(xì)節(jié),用抽象完成對(duì)象的創(chuàng)建
          ????????????IDatabase dal = container.Resolve();
          ????????????dal.Connection("con");


          通過上邊的一頓操作,我們做了什么事呢?我們完成了一個(gè)大的飛躍,通常創(chuàng)建對(duì)象我們是直接new一個(gè),現(xiàn)在我們是通過一個(gè)第三方的容器為我們創(chuàng)建對(duì)象,并且我們不用依賴于細(xì)節(jié),通過接口的類型完成對(duì)象的創(chuàng)建,當(dāng)我們要將SqlserverDal替換為MysqlDal時(shí),我們只需要在注冊(cè)的時(shí)候?qū)qlserverDal替換為MysqlDal即可

          ?二.升級(jí)改造容器(解決參數(shù)問題)

          上面我們將傳統(tǒng)對(duì)象創(chuàng)建的方式,改為使用第三方容器來幫我們完成對(duì)象的創(chuàng)建。但這個(gè)容器考慮的還不是那么的全面,例如有參構(gòu)造的問題,以及對(duì)象的依賴問題我們還沒有考慮到,接下來我們繼續(xù)完善這個(gè)容器,這里我們先不考慮多個(gè)構(gòu)造函數(shù)的問題。這里先解決只有一個(gè)構(gòu)造函數(shù)場(chǎng)景的參數(shù)問題

          1.構(gòu)造函數(shù)只有一個(gè)參數(shù)的情況


          //創(chuàng)建對(duì)象通過傳遞的類型進(jìn)行匹配
          ????????public?IT Resolve()
          ????????{
          ????????????string?key = typeof(IT).FullName;
          ????????????Type type?= this.TypeDictionary[key]; //獲取要?jiǎng)?chuàng)建對(duì)象的類型
          ????????????var?ctor = type.GetConstructors()[0]; //這里先考慮只有一個(gè)構(gòu)造函數(shù)的場(chǎng)景
          ???????????
          ????????????//一個(gè)參數(shù)的形式
          ????????????var?paraList = ctor.GetParameters();
          ????????????var?para = paraList[0];
          ????????????Type paraInterfaceType = para.ParameterType;
          ????????????Type paraType = this.TypeDictionary[paraInterfaceType.FullName]; //還是要先獲取依賴對(duì)象的類型
          ????????????object oPara = Activator.CreateInstance(paraType); //創(chuàng)建參數(shù)中所依賴的對(duì)象
          ????????????return?(IT)Activator.CreateInstance(type,oPara); //創(chuàng)建對(duì)象并傳遞所依賴的對(duì)象
          ????????}


          2.構(gòu)造函數(shù)多參數(shù)的情況

          上面我們解決了構(gòu)造函數(shù)只有一個(gè)參數(shù)的問題,我們是通過構(gòu)造函數(shù)的類型創(chuàng)建一個(gè)對(duì)象,并將這個(gè)對(duì)象作為參數(shù)傳遞到要實(shí)例化的對(duì)象中。那么多參數(shù)我們就需要?jiǎng)?chuàng)建多個(gè)參數(shù)的對(duì)象傳遞到要實(shí)例的對(duì)象中


          //創(chuàng)建對(duì)象通過傳遞的類型進(jìn)行匹配
          ????????public?IT Resolve()
          ????????{
          ????????????string?key = typeof(IT).FullName;
          ????????????Type type = this.TypeDictionary[key]; //獲取要?jiǎng)?chuàng)建對(duì)象的類型
          ????????????var?ctor = type.GetConstructors()[0]; //這里先考慮只有一個(gè)構(gòu)造函數(shù)的場(chǎng)景
          ???????????
          ????????????//多個(gè)參數(shù)的形式
          ????????????List<object> paraList = new?List<object>(); //聲明一個(gè)list來存儲(chǔ)參數(shù)類型的對(duì)象
          ????????????foreach?(var?para in?ctor.GetParameters())
          ????????????{
          ????????????????Type paraInterfaceType = para.ParameterType;
          ????????????????Type paraType = this.TypeDictionary[paraInterfaceType.FullName];
          ????????????????object?oPara = Activator.CreateInstance(paraType);
          ????????????????paraList.Add(oPara);
          ????????????}

          ????????????return?(IT)Activator.CreateInstance(type, paraList.ToArray()); //創(chuàng)建對(duì)象并傳遞所依賴的對(duì)象數(shù)組
          ????????}


          ?3.解決對(duì)象的循環(huán)依賴問題

          通過上面的兩步操作,我們已經(jīng)能對(duì)構(gòu)造函數(shù)中的參數(shù)初始化對(duì)象并傳遞到要實(shí)例的對(duì)象中,但這只是一個(gè)層級(jí)的。我們剛才做的只是解決了這么一個(gè)問題,假設(shè)我們要?jiǎng)?chuàng)建A對(duì)象,A對(duì)象依賴于B對(duì)象。我們做的就是創(chuàng)建了B對(duì)象作為參數(shù)傳遞給A并創(chuàng)建A對(duì)象,這只是一個(gè)層級(jí)的。當(dāng)B對(duì)象又依賴于C對(duì)象,C對(duì)象又依賴于D對(duì)象,這么一直循環(huán)下去。這樣的場(chǎng)景我們?cè)撛趺唇鉀Q呢?下面我們將通過遞歸的方式來解決這一問題


          //創(chuàng)建對(duì)象通過傳遞的類型進(jìn)行匹配
          ????????public?IT Resolve()
          ????????{
          ????????????return?(IT)this.ResolveObject(typeof(IT));
          ????????}

          ????????//通過遞歸的方式創(chuàng)建多層級(jí)的對(duì)象
          ????????private?object?ResolveObject(Type abstractType)
          ????????
          {
          ????????????string?key = abstractType.FullName;
          ????????????Type type = this.TypeDictionary[key]; //獲取要?jiǎng)?chuàng)建對(duì)象的類型
          ????????????var?ctor = type.GetConstructors()[0];
          ????????????//多個(gè)參數(shù)的形式
          ????????????List<object> paraList = new?List<object>();
          ????????????foreach?(var?para in?ctor.GetParameters())
          ????????????{
          ????????????????Type paraInterfaceType = para.ParameterType;
          ????????????????Type paraType = this.TypeDictionary[paraInterfaceType.FullName];
          ????????????????object?oPara = ResolveObject(paraInterfaceType); //自已調(diào)用自己,實(shí)現(xiàn)遞歸操作,完成各個(gè)層級(jí)對(duì)象的創(chuàng)建
          ????????????????paraList.Add(oPara);
          ????????????}

          ????????????return?(object)Activator.CreateInstance(type, paraList.ToArray());

          ????????}


          三.繼續(xù)升級(jí)(考慮多個(gè)構(gòu)造函數(shù)的問題)

          上面我們只是考慮了只有一個(gè)構(gòu)造函數(shù)的問題,那初始化的對(duì)象有多個(gè)構(gòu)造函數(shù)我們?cè)撊绾翁幚砟兀覀兛梢韵馎utofac那樣選擇一個(gè)參數(shù)最多的構(gòu)造函數(shù),也可以像ServiceCollection那樣選擇一個(gè)參數(shù)的超集來進(jìn)行構(gòu)造,當(dāng)然我們也可以聲明一個(gè)特性,那個(gè)構(gòu)造函數(shù)中標(biāo)記了這個(gè)特性,我們就采用那個(gè)構(gòu)造函數(shù)。


          //通過遞歸的方式創(chuàng)建多層級(jí)的對(duì)象
          ????????private?object ResolveObject(Type abstractType)
          ????????{
          ????????????string?key = abstractType.FullName;
          ????????????Type type?= this.TypeDictionary[key]; //獲取要?jiǎng)?chuàng)建對(duì)象的類型
          ????????????var?ctorArray = type.GetConstructors(); //獲取對(duì)象的所有構(gòu)造函數(shù)
          ????????????ConstructorInfo ctor = null;
          ????????????//判斷構(gòu)造函數(shù)中是否標(biāo)記了HTAttribute這個(gè)特性
          ????????????if?(ctorArray.Count(c?=>?c.IsDefined(typeof(HTAttribute), true)) > 0)
          ????????????{
          ????????????????//若標(biāo)記了HTAttribute特性,默認(rèn)就采用這個(gè)構(gòu)造函數(shù)
          ????????????????ctor = ctorArray.FirstOrDefault(c?=>?c.IsDefined(typeof(HTAttribute), true));
          ????????????}
          ????????????else
          ????????????{
          ????????????????//若都沒有標(biāo)記特性,那就采用構(gòu)造函數(shù)中參數(shù)最多的構(gòu)造函數(shù)
          ????????????????ctor = ctorArray.OrderByDescending(c?=>?c.GetParameters().Length).FirstOrDefault();
          ????????????}
          ?????
          ????????????//多個(gè)參數(shù)的形式
          ????????????List paraList = new?List();
          ????????????foreach (var?para in?ctor.GetParameters())
          ????????????{
          ????????????????Type paraInterfaceType = para.ParameterType;
          ????????????????Type paraType = this.TypeDictionary[paraInterfaceType.FullName];
          ????????????????object oPara = ResolveObject(paraInterfaceType); //自已調(diào)用自己,實(shí)現(xiàn)遞歸操作,完成各個(gè)層級(jí)對(duì)象的創(chuàng)建
          ????????????????paraList.Add(oPara);
          ????????????}

          ????????????return?(object)Activator.CreateInstance(type, paraList.ToArray());
          ???
          ????????}


          上面的操作我們通過依賴注入的方式完成了對(duì)容器的升級(jí),那么依賴注入到底是啥呢?

          依賴注入(Dependency Injection,簡稱DI)就是構(gòu)造A對(duì)象時(shí),需要依賴B對(duì)象,那么就先構(gòu)造B對(duì)象作為參數(shù)傳遞到A對(duì)象,這種對(duì)象初始化并注入的技術(shù)就叫做依賴注入。IOC是一種設(shè)計(jì)模式,程序架構(gòu)的目標(biāo)。DI是IOC的實(shí)現(xiàn)手段

          6109a453f69ed3886692c97fdf46d47c.webp


          cb2946655a0b8cdd3dc23afaf2f55347.webp

          ??? ?




          感謝點(diǎn)贊支持下哈?10a29820dfe7254ab4d983493959bdad.webp



          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    亚洲AV无码国产成人久久 | 最新中文字幕在线 | 无码三级视频 | 香蕉视频在线观看网站资源 | 日本18禁网站 |