成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

徒手?jǐn)]框架--實現(xiàn)IoC

rottengeek / 1405人閱讀

摘要:從而能夠進(jìn)一步深入了解框架。至此我們框架開發(fā)完成。雖然說閱讀源碼是了解框架的最終手段。但是框架作為一個生產(chǎn)框架,為了保證通用和穩(wěn)定,源碼必定是高度抽象,且處理大量細(xì)節(jié)。下一篇文章應(yīng)該會是徒手?jǐn)]框架實現(xiàn)。

原文地址:https://www.xilidou.com/2018/...

Spring 作為 J2ee 開發(fā)事實上的標(biāo)準(zhǔn),是每個Java開發(fā)人員都需要了解的框架。但是Spring 的 IoC 和 Aop 的特性,對于初級的Java開發(fā)人員來說還是比較難于理解的。所以我就想寫一系列的文章給大家講解這些特性。從而能夠進(jìn)一步深入了解 Spring 框架。

讀完這篇文章,你將會了解:

什么是依賴注入和控制反轉(zhuǎn)

Ioc有什么用

Spring的 Ioc 是怎么實現(xiàn)的

按照Spring的思路開發(fā)一個簡單的Ioc框架

IoC 是什么?

wiki百科的解釋是:

控制反轉(zhuǎn)(Inversion of Control,縮寫為IoC),是面向?qū)ο缶幊讨械囊环N設(shè)計原則,可以用來減低計算機(jī)代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)。通過控制反轉(zhuǎn),對象在被創(chuàng)建的時候,由一個調(diào)控系統(tǒng)內(nèi)所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。
Ioc 有什么用?

看完上面的解釋你一定沒有理解什么是 Ioc,因為是第一次看見上面的話也覺得云里霧里。

不過通過上面的描述我們可以大概的了解到,使用IoC的目的是為了解耦。也就是說IoC 是解耦的一種方法。

我們知道Java 是一門面向?qū)ο蟮恼Z言,在 Java 中 Everything is Object,我們的程序就是由若干對象組成的。當(dāng)我們的項目越來越大,合作的開發(fā)者越來越多的時候,我們的類就會越來越多,類與類之間的引用就會成指數(shù)級的增長。如下圖所示:

這樣的工程簡直就是災(zāi)難,如果我們引入 Ioc 框架。由框架來維護(hù)類的生命周期和類之間的引用。我們的系統(tǒng)就會變成這樣:

這個時候我們發(fā)現(xiàn),我們類之間的關(guān)系都由 IoC 框架負(fù)責(zé)維護(hù)類,同時將類注入到需要的類中。也就是類的使用者只負(fù)責(zé)使用,而不負(fù)責(zé)維護(hù)。把專業(yè)的事情交給專業(yè)的框架來完成。大大的減少開發(fā)的復(fù)雜度。

用一個類比來理解這個問題。Ioc 框架就是我們生活中的房屋中介,首先中介會收集市場上的房源,分別和各個房源的房東建立聯(lián)系。當(dāng)我們需要租房的時候,并不需要我們四處尋找各類租房信息。我們直接找房屋中介,中介就會根據(jù)你的需求提供相應(yīng)的房屋信息。大大提升了租房的效率,減少了你與各類房東之間的溝通次數(shù)。

Spring 的 IoC 是怎么實現(xiàn)的

了解Spring框架最直接的方法就閱讀Spring的源碼。但是Spring的代碼抽象的層次很高,且處理的細(xì)節(jié)很高。對于大多數(shù)人來說不是太容易理解。我讀了Spirng的源碼以后以我的理解做一個總結(jié),Spirng IoC 主要是以下幾個步驟。

1. 初始化 IoC 容器。
2. 讀取配置文件。
3. 將配置文件轉(zhuǎn)換為容器識別對的數(shù)據(jù)結(jié)構(gòu)(這個數(shù)據(jù)結(jié)構(gòu)在Spring中叫做 BeanDefinition) 
4. 利用數(shù)據(jù)結(jié)構(gòu)依次實例化相應(yīng)的對象
5. 注入對象之間的依賴關(guān)系
自己實現(xiàn)一個IoC框架

為了方便,我們參考 Spirng 的 IoC 實現(xiàn),去除所有與核心原理無關(guān)的邏輯。極簡的實現(xiàn) IoC 的框架。 項目使用 json 作為配置文件。使用 maven 管理 jar 包的依賴。

在這個框架中我們的對象都是單例的,并不支持Spirng的多種作用域??蚣艿膶崿F(xiàn)使用了cglib 和 Java 的反射。項目中我還使用了 lombok 用來簡化代碼。

下面我們就來編寫 IoC 框架吧。

首先我們看看這個框架的基本結(jié)構(gòu):

從宏觀上觀察一下這個框架,包含了3個package、在包 bean 中定義了我們框架的數(shù)據(jù)結(jié)構(gòu)。core 是我們框架的核心邏輯所在。utils 是一些通用工具類。接下來我們就逐一講解一下:

1. bean 定義了框架的數(shù)據(jù)結(jié)構(gòu)

BeanDefinition 是我們項目的核心數(shù)據(jù)結(jié)構(gòu)。用于描述我們需要 IoC 框架管理的對象。

@Data
@ToString
public class BeanDefinition {

    private String name;

    private String className;

    private String interfaceName;

    private List constructorArgs;

    private List propertyArgs;

}

包含了對象的 name,class的名稱。如果是接口的實現(xiàn),還有該對象實現(xiàn)的接口。以及構(gòu)造函數(shù)的傳參的列表 constructorArgs 和需要注入的參數(shù)列表 `propertyArgs。

2. 再看看我們的工具類包里面的對象:

ClassUtils 負(fù)責(zé)處理 Java 類的加載,代碼如下:

public class ClassUtils {
    public static ClassLoader getDefultClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
    public static Class loadClass(String className){
        try {
            return getDefultClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

我們只寫了一個方法,就是通過 className 這個參數(shù)獲取對象的 Class。

BeanUtils 負(fù)責(zé)處理對象的實例化,這里我們使用了 cglib 這個工具包,代碼如下:

public class BeanUtils {
    public static  T instanceByCglib(Class clz,Constructor ctr,Object[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clz);
        enhancer.setCallback(NoOp.INSTANCE);
        if(ctr == null){
            return (T) enhancer.create();
        }else {
            return (T) enhancer.create(ctr.getParameterTypes(),args);
        }
    }
}

ReflectionUtils 主要通過 Java 的反射原理來完成對象的依賴注入:

public class ReflectionUtils {

    public static void injectField(Field field,Object obj,Object value) throws IllegalAccessException {
        if(field != null) {
            field.setAccessible(true);
            field.set(obj, value);
        }
    }
}

injectField(Field field,Object obj,Object value) 這個方法的作用就是,設(shè)置 obj 的 field 為 value。

JsonUtils 的作用就是為了解析我們的json配置文件。代碼比較長,與我們的 IoC 原理關(guān)系不大,感興趣的同學(xué)可以自行從github上下載代碼看看。

有了這幾個趁手的工具,我們就可以開始完成 Ioc 框架的核心代碼了。

3. 核心邏輯

我的 IoC 框架,目前只支持一種 ByName 的注入。所以我們的 BeanFactory 就只有一個方法:

public interface BeanFactory {
    Object getBean(String name) throws Exception;
}

然后我們實現(xiàn)了這個方法:

public class BeanFactoryImpl implements BeanFactory{

    private static final ConcurrentHashMap beanMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap beanDefineMap= new ConcurrentHashMap<>();

    private static final Set beanNameSet = Collections.synchronizedSet(new HashSet<>());

    @Override
    public Object getBean(String name) throws Exception {
        //查找對象是否已經(jīng)實例化過
        Object bean = beanMap.get(name);
        if(bean != null){
            return bean;
        }
        //如果沒有實例化,那就需要調(diào)用createBean來創(chuàng)建對象
        bean =  createBean(beanDefineMap.get(name));
        
        if(bean != null) {

            //對象創(chuàng)建成功以后,注入對象需要的參數(shù)
            populatebean(bean);
            
            //再把對象存入Map中方便下次使用。
            beanMap.put(name,bean;
        }

        //結(jié)束返回
        return bean;
    }

    protected void registerBean(String name, BeanDefinition bd){
        beanDefineMap.put(name,bd);
        beanNameSet.add(name);
    }

    private Object createBean(BeanDefinition beanDefinition) throws Exception {
        String beanName = beanDefinition.getClassName();
        Class clz = ClassUtils.loadClass(beanName);
        if(clz == null) {
            throw new Exception("can not find bean by beanName");
        }
        List constructorArgs = beanDefinition.getConstructorArgs();
        if(constructorArgs != null && !constructorArgs.isEmpty()){
            List objects = new ArrayList<>();
            for (ConstructorArg constructorArg : constructorArgs) {
                objects.add(getBean(constructorArg.getRef()));
            }
            return BeanUtils.instanceByCglib(clz,clz.getConstructor(),objects.toArray());
        }else {
            return BeanUtils.instanceByCglib(clz,null,null);
        }
    }

    private void populatebean(Object bean) throws Exception {
        Field[] fields = bean.getClass().getSuperclass().getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                String beanName = field.getName();
                beanName = StringUtils.uncapitalize(beanName);
                if (beanNameSet.contains(field.getName())) {
                    Object fieldBean = getBean(beanName);
                    if (fieldBean != null) {
                        ReflectionUtils.injectField(field,bean,fieldBean);
                    }
                }
            }
        }
    }
}

首先我們看到在 BeanFactory 的實現(xiàn)中。我們有兩 HashMap,beanMap 和 beanDefineMap。 beanDefineMap 存儲的是對象的名稱和對象對應(yīng)的數(shù)據(jù)結(jié)構(gòu)的映射。beanMap 用于保存 beanName和實例化之后的對象。

容器初始化的時候,會調(diào)用 BeanFactoryImpl.registerBean 方法。把 對象的 BeanDefination 數(shù)據(jù)結(jié)構(gòu),存儲起來。

當(dāng)我們調(diào)用 getBean() 的方法的時候。會先到 beanMap 里面查找,有沒有實例化好的對象。如果沒有,就會去beanDefineMap查找這個對象對應(yīng)的 BeanDefination。再利用DeanDefination去實例化一個對象。

對象實例化成功以后,我們還需要注入相應(yīng)的參數(shù),調(diào)用 populatebean()這個方法。在 populateBean 這個方法中,會掃描對象里面的Field,如果對象中的 Field 是我們IoC容器管理的對象,那就會調(diào)用 我們上文實現(xiàn)的 ReflectionUtils.injectField來注入對象。

一切準(zhǔn)備妥當(dāng)之后,我們對象就完成了整個 IoC 流程。最后這個對象放入 beanMap 中,方便下一次使用。

所以我們可以知道 BeanFactory 是管理和生成對象的地方。

4. 容器

我們所謂的容器,就是對BeanFactory的擴(kuò)展,負(fù)責(zé)管理 BeanFactory。我們的這個IoC 框架使用 Json 作為配置文件,所以我們?nèi)萜骶兔麨?JsonApplicationContext。當(dāng)然之后你愿意實現(xiàn) XML 作為配置文件的容器你就可以自己寫一個 XmlApplicationContext,如果基于注解的容器就可以叫AnnotationApplcationContext。這些實現(xiàn)留個大家去完成。

我們看看 ApplicationContext 的代碼:

public class JsonApplicationContext extends BeanFactoryImpl{
    private String fileName;
    public JsonApplicationContext(String fileName) {
        this.fileName = fileName;
    }
    public void init(){
        loadFile();
    }
    private void loadFile(){
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        List beanDefinitions = JsonUtils.readValue(is,new TypeReference>(){});
        if(beanDefinitions != null && !beanDefinitions.isEmpty()) {
            for (BeanDefinition beanDefinition : beanDefinitions) {
                registerBean(beanDefinition.getName(), beanDefinition);
            }
        }
    }
}

這個容器的作用就是 讀取配置文件。將配置文件轉(zhuǎn)換為容器能夠理解的 BeanDefination。然后使用 registerBean 方法。注冊這個對象。

至此,一個簡單版的 IoC 框架就完成。

5. 框架的使用

我們寫一個測試類來看看我們這個框架怎么使用:

首先我們有三個對象

public class Hand {
    public void waveHand(){
        System.out.println("揮一揮手");
    }
}

public class Mouth {
    public void speak(){
        System.out.println("say hello world");
    }
}

public class Robot {
    //需要注入 hand 和 mouth 
    private Hand hand;
    private Mouth mouth;

    public void show(){
        hand.waveHand();
        mouth.speak();
    }
}

我們需要為我們的 Robot 機(jī)器人注入 hand 和 mouth。

配置文件:

[
  {
    "name":"robot",
    "className":"com.xilidou.framework.ioc.entity.Robot"
  },
  {
    "name":"hand",
    "className":"com.xilidou.framework.ioc.entity.Hand"
  },
  {
    "name":"mouth",
    "className":"com.xilidou.framework.ioc.entity.Mouth"
  }
]

這個時候?qū)懸粋€測試類:

public class Test {
    public static void main(String[] args) throws Exception {
        JsonApplicationContext applicationContext = new JsonApplicationContext("application.json");
        applicationContext.init();
        Robot aiRobot = (Robot) applicationContext.getBean("robot");
        aiRobot.show();
    }
}

運(yùn)行以后輸出:

揮一揮手
say hello world

Process finished with exit code 0

可以看到我們成功的給我的 aiRobot 注入了 hand 和 mouth。

至此我們 Ioc 框架開發(fā)完成。

總結(jié)

這篇文章讀完以后相信你一定也實現(xiàn)了一個簡單的 IoC 框架。

雖然說閱讀源碼是了解框架的最終手段。但是 Spring 框架作為一個生產(chǎn)框架,為了保證通用和穩(wěn)定,源碼必定是高度抽象,且處理大量細(xì)節(jié)。所以 Spring 的源碼閱讀起來還是相當(dāng)困難。希望這篇文章能夠幫助理解 Spring Ioc 的實現(xiàn)。

下一篇文章 應(yīng)該會是 《徒手?jǐn)]框架--實現(xiàn)AOP》。

github 地址

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68342.html

相關(guān)文章

  • 徒手框架--實現(xiàn)Aop

    摘要:只實現(xiàn)了基于方法的攔截器。實現(xiàn)了一個遞歸的調(diào)用,直到執(zhí)行完所有的攔截器。目標(biāo)對象攔截器列表這個就是我們框架能夠理解的數(shù)據(jù)結(jié)構(gòu),這個時候問題就變成了對于哪個目標(biāo),增加哪些攔截器。 原文地址:犀利豆的博客 上一講我們講解了Spring 的 IoC 實現(xiàn)。大家可以去我的博客查看點(diǎn)擊鏈接,這一講我們繼續(xù)說說 Spring 的另外一個重要特性 AOP。之前在看過的大部分教程,對于Spring ...

    weij 評論0 收藏0
  • 徒手框架--高并發(fā)環(huán)境下的請求合并

    摘要:我們就可以將這些請求合并,達(dá)到一定數(shù)量我們統(tǒng)一提交??偨Y(jié)一個比較生動的例子給大家講解了一些多線程的具體運(yùn)用。學(xué)習(xí)多線程應(yīng)該多思考多動手,才會有比較好的效果。地址徒手?jǐn)]框架系列文章地址徒手?jǐn)]框架實現(xiàn)徒手?jǐn)]框架實現(xiàn) 原文地址:https://www.xilidou.com/2018/01/22/merge-request/ 在高并發(fā)系統(tǒng)中,我們經(jīng)常遇到這樣的需求:系統(tǒng)產(chǎn)生大量的請求,但是這...

    劉東 評論0 收藏0
  • JAVA 中的 CAS

    摘要:我們繼續(xù)看代碼的意思是這個是一段內(nèi)嵌匯編代碼。也就是在語言中使用匯編代碼。就是匯編版的比較并交換。就是保證在多線程情況下,不阻塞線程的填充和消費(fèi)。微觀上看匯編的是實現(xiàn)操作系統(tǒng)級別的原子操作的基石。 原文地址:https://www.xilidou.com/2018/02/01/java-cas/ CAS 是現(xiàn)代操作系統(tǒng),解決并發(fā)問題的一個重要手段,最近在看 eureka 的源碼的時候。...

    CocoaChina 評論0 收藏0
  • 徒手一個簡單的RPC框架

    摘要:徒手?jǐn)]一個簡單的框架之前在牛逼哄哄的框架,底層到底什么原理得知了遠(yuǎn)程過程調(diào)用簡單來說就是調(diào)用遠(yuǎn)程的服務(wù)就像調(diào)用本地方法一樣,其中用到的知識有序列化和反序列化動態(tài)代理網(wǎng)絡(luò)傳輸動態(tài)加載反射這些知識點(diǎn)。 徒手?jǐn)]一個簡單的RPC框架 之前在牛逼哄哄的 RPC 框架,底層到底什么原理得知了RPC(遠(yuǎn)程過程調(diào)用)簡單來說就是調(diào)用遠(yuǎn)程的服務(wù)就像調(diào)用本地方法一樣,其中用到的知識有序列化和反序列化、動態(tài)...

    Gemini 評論0 收藏0
  • 徒手一個 Spring Boot 中的 Starter ,解密自動化配置黑魔法!

    摘要:先來看代碼吧,一會松哥再慢慢解釋關(guān)于這一段自動配置,解釋如下首先注解表明這是一個配置類。本文的案例,松哥已經(jīng)上傳到上了,地址。我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中。Starter 為我們帶來了眾多的自動化配置,有了這些自動化配置,我們可以不費(fèi)吹灰之力就能搭建一個生產(chǎn)級開發(fā)環(huán)境,有的小伙伴會覺得這個 Starter 好神奇呀!其實 Starter 也都...

    xiaochao 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<