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

        共 6873字,需瀏覽 14分鐘

         ·

        2020-07-27 09:27

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

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


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

        ? 作?|??hello-*-world

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


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

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

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

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

        ????????}


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

        一.容器雛形

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


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

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


        簡單調(diào)用


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


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

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

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

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


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


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

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


        //創(chuàng)建對象通過傳遞的類型進(jìn)行匹配
        ????????public?IT Resolve()
        ????????{
        ????????????string?key = typeof(IT).FullName;
        ????????????Type type = this.TypeDictionary[key]; //獲取要創(chuàng)建對象的類型
        ????????????var?ctor = type.GetConstructors()[0]; //這里先考慮只有一個構(gòu)造函數(shù)的場景
        ???????????
        ????????????//多個參數(shù)的形式
        ????????????List<object> paraList = new?List<object>(); //聲明一個list來存儲參數(shù)類型的對象
        ????????????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)建對象并傳遞所依賴的對象數(shù)組
        ????????}


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

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


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

        ????????//通過遞歸的方式創(chuàng)建多層級的對象
        ????????private?object?ResolveObject(Type abstractType)
        ????????
        {
        ????????????string?key = abstractType.FullName;
        ????????????Type type = this.TypeDictionary[key]; //獲取要創(chuàng)建對象的類型
        ????????????var?ctor = type.GetConstructors()[0];
        ????????????//多個參數(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)用自己,實現(xiàn)遞歸操作,完成各個層級對象的創(chuàng)建
        ????????????????paraList.Add(oPara);
        ????????????}

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

        ????????}


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

        上面我們只是考慮了只有一個構(gòu)造函數(shù)的問題,那初始化的對象有多個構(gòu)造函數(shù)我們該如何處理呢,我們可以像Autofac那樣選擇一個參數(shù)最多的構(gòu)造函數(shù),也可以像ServiceCollection那樣選擇一個參數(shù)的超集來進(jìn)行構(gòu)造,當(dāng)然我們也可以聲明一個特性,那個構(gòu)造函數(shù)中標(biāo)記了這個特性,我們就采用那個構(gòu)造函數(shù)。


        //通過遞歸的方式創(chuàng)建多層級的對象
        ????????private?object ResolveObject(Type abstractType)
        ????????{
        ????????????string?key = abstractType.FullName;
        ????????????Type type?= this.TypeDictionary[key]; //獲取要創(chuàng)建對象的類型
        ????????????var?ctorArray = type.GetConstructors(); //獲取對象的所有構(gòu)造函數(shù)
        ????????????ConstructorInfo ctor = null;
        ????????????//判斷構(gòu)造函數(shù)中是否標(biāo)記了HTAttribute這個特性
        ????????????if?(ctorArray.Count(c?=>?c.IsDefined(typeof(HTAttribute), true)) > 0)
        ????????????{
        ????????????????//若標(biāo)記了HTAttribute特性,默認(rèn)就采用這個構(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();
        ????????????}
        ?????
        ????????????//多個參數(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)用自己,實現(xiàn)遞歸操作,完成各個層級對象的創(chuàng)建
        ????????????????paraList.Add(oPara);
        ????????????}

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


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

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

        6109a453f69ed3886692c97fdf46d47c.webp


        cb2946655a0b8cdd3dc23afaf2f55347.webp

        ??? ?




        感謝點贊支持下哈?10a29820dfe7254ab4d983493959bdad.webp



        瀏覽 36
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
            
            

                      • 男人的天堂黄色视频 | 9l农村站街老熟女露脸 | 做爱视频在线播放 | 日韩aⅴ网站 | 激情五月伊人 |