Spring系列之IOC的原理及手動實現


目錄

導語

Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。也是幾乎所有Java工作者必須要掌握的框架之一,其優秀的設計思想以及其代碼實現上的藝術也是我們需要掌握的。要學習Spring,除了在我們的項目中使用之外,也需要對它的源碼進行研讀,但是Spring的實現涵蓋的知識很多,在加上其中的類的數量也是非常的多,在我們閱讀源碼時可能會在幾十個類之間穿插閱讀,很有可能一不小心就導致思維混亂。有鑒於此,我這里先對Spring中的幾個重要的模塊進行一個手動的簡易實現,一是熟悉這些模塊的原理,同時也是仿造Spring中的結構來對后面閱讀源碼打下基礎。

IOC(Inversion of Control)

Inversion of Control即控制反轉,其意思是將我們之前由客戶端代碼來創建的對象交由IOC容器來進行控制,對象的創建,初始化以及后面的管理都由IOC完成。

IOC的好處

  1. 解耦:IOC的出現解決了類於類之間的耦合,我們在Web開發的Servlet時代,如果一個Servlet需要依賴另一個類的某些實現,那么我們需要在當前類對依賴的類進行創建和初始化,如果其他類也依賴了這個類,那也需要進行創建和初始化,而交給了IOC來管理的話,那么在需要的時候只需向IOC進行申請,而不需要重復的創建和初始化。當然,IOC也允許每次都重新創建一個新的對象。
  2. 方便與AOP進行配合:AOP也是一個使用十分頻繁的功能,通過IOC可以十分方便的與AOP進行配合。

IOC中設計的設計模式

工廠模式。IOC容器來負責創建管理類實例對象,在需要時向IOC進行申請,從IOC中獲取。所以IOC容器也稱為bean工廠。

工廠模式是一種比較簡單易懂的設計模式,這里就不在介紹了,如果有需要的可以看看這個:工廠模式

IOC的手動實現

Bean定義

IOC的主要的功能便是對Bean進行管理,包括創建、初始化、管理以及銷魂的工作。首先我們面對的問題就是我們怎么讓IOC能夠創建一個Bean?為了創建Bean我們需要提供一些什么?

如何創建Bean

在不手動通過new關鍵字創建的情況下創建類實例的對象方法有兩種:

  1. 反射:通過反射的方法可以創建類的實例:clazz.getClass().newInstance();
  2. 工廠模式:工廠模式可以讓我們在不接觸實例類的情況下創建出實例。
public class PersonFactory{
    public Person getPerson(){
        return new Person();
    }
}
為了創建Bean我們需要提供什么

通過分析上面的兩種方法可以輕松得出答案。

對於反射的方式我們僅需提供實例的Class對象。

對於工廠方法我們需要提供的就是創建該類的工廠名(factoryName)和方法名(methodName);

除了創建bean還需要做些什么

IOC容器是對bean的整個生命周期進行管理,除了創建之外還需要對bean進行初始化,以及不需要時對bean進行銷毀的工作(如釋放資源等)。所以我們還需要提供初始化和銷毀等操作。

到這里創建bean需要的基本分析完了,看類圖:

BeanDefinition

Bean工廠

Bean的定義解決了,但是這個bean定義以及創建好的Bean實例放在哪里呢,我們需要一個統一的地方來存放這些東西以方便我們要用的時候方便取。

我們定義一個Bean工廠來存放bean,在需要的時候懂bean工廠中取即可,bean工廠對外提供的也僅僅是一個獲取bean的方法即可,由於bean的類型不定,所以返回值定位Object。

BeanFactory

注冊Bean定義

到了現在我們有了創建bean的Bean定義,有了存放和管理bean的Bean工廠,現在需要考慮的事怎么來聯系這兩個類,我們還需要另外一個接口,接口的功能是讓我們能注冊和獲取bean定義,這里我們通過beanName來區分不同的bean。

BeanDefinitionRegistry

代碼實現

到這里我們實現一個簡易的IOC容器的需要的東西基本准備完成了,看下基本類圖:

類關系

基本代碼實現:

DefaultBeanDefinition:

public class DefaultBeanDefinition implements BeanDefinition{

    private Class<?> clazz;

    private String beanFactoryName;

    private String createBeanMethodName;

    private String staticCreateBeanMethodName;

    private String beanInitMethodName;

    private String beanDestoryMethodName;

    private boolean isSingleton;

    // setter
    

    public void setSingleton(boolean singleton) {
        isSingleton = singleton;
    }

    @Override
    public Class<?> getBeanClass() {
        return this.clazz;
    }

    @Override
    public String getBeanFactory() {
        return this.beanFactoryName;
    }

    @Override
    public String getCreateBeanMethod() {
        return this.createBeanMethodName;
    }

    @Override
    public String getStaticCreateBeanMethod() {
        return this.staticCreateBeanMethodName;
    }

    @Override
    public String getBeanInitMethodName() {
        return this.beanInitMethodName;
    }

    @Override
    public String getBeanDestoryMethodName() {
        return this.beanDestoryMethodName;
    }

    @Override
    public String getScope() {
        return this.isSingleton?BeanDefinition.SINGLETION :BeanDefinition.PROTOTYPE;
    }

    @Override
    public boolean isSingleton() {
        return this.isSingleton;
    }

    @Override
    public boolean isPrototype() {
        return !this.isSingleton;
    }
}

DefaultBeanFactory:

public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {

    private Log log = LogFactory.getLog(this.getClass());

    //ConcurrentHashMap應對並發環境
    private Map<String, BeanDefinition> bdMap = new ConcurrentHashMap<>();

    private Map<String, Object> beanMap = new ConcurrentHashMap<>();

    @Override
    public void register(BeanDefinition bd, String beanName) {

        Assert.assertNotNull("beanName不能為空 beanName", beanName);
        Assert.assertNotNull("BeanDefinition不能為空", bd);

        if(bdMap.containsKey(beanName)){
            log.info("[" + beanName + "]已經存在");
        }

        if(!bd.validate()){
            log.info("BeanDefinition不合法");
        }

        if(!bdMap.containsKey(beanName)){
            bdMap.put(beanName, bd);
        }
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return bdMap.containsKey(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        if(!bdMap.containsKey(beanName)){
            log.info("[" + beanName + "]不存在");
        }
        return bdMap.get(beanName);
    }

    public Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException {
        if(!beanMap.containsKey(beanName)){
            log.info("[" + beanName + "]不存在");
        }

        Object instance = beanMap.get(beanName);

        if(instance != null){
            return instance;
        }

        //不存在則進行創建
        if(!this.bdMap.containsKey(beanName)){
            log.info("不存在名為:[" + beanName + "]的bean定義");
        }

        BeanDefinition bd = this.bdMap.get(beanName);

        Class<?> beanClass = bd.getBeanClass();

        if(beanClass != null){
            instance = createBeanByConstruct(beanClass);
            if(instance == null){
                instance = createBeanByStaticFactoryMethod(bd);
            }
        }else if(instance == null && StringUtils.isNotBlank(bd.getStaticCreateBeanMethod())){
            instance = createBeanByFactoryMethod(bd);
        }

        this.doInit(bd, instance);

        if(instance != null && bd.isSingleton()){
            beanMap.put(beanName, instance);
        }

        return instance;
    }

    private void doInit(BeanDefinition bd, Object instance) {
        Class<?> beanClass = instance.getClass();
        if(StringUtils.isNotBlank(bd.getBeanInitMethodName())){
            try {
                Method method = beanClass.getMethod(bd.getBeanInitMethodName(), null);
                method.invoke(instance, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 構造方法創建實例
     * @param beanClass
     * @return
     */
    private Object createBeanByConstruct(Class<?> beanClass) {
        Object instance = null;
        try {
            instance = beanClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 普通工廠方法創建實例
     * @param bd
     * @return
     */
    private Object createBeanByFactoryMethod(BeanDefinition bd) {
        Object instance = null;
        try {
            //獲取工廠類
            Object factory = doGetBean(bd.getBeanFactory());
            //獲取創建實例的方法
            Method method = factory.getClass().getMethod(bd.getCreateBeanMethod());
            //執行方法
            instance = method.invoke(factory, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 靜態方法創建實例
     * @param bd
     * @return
     */
    private Object createBeanByStaticFactoryMethod(BeanDefinition bd) {
        Object instance = null;
        try {
            Class<?> beanClass = bd.getBeanClass();
            //獲取創建實例的方法
            Method method = beanClass.getMethod(bd.getStaticCreateBeanMethod());
            instance = method.invoke(beanClass, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    @Override
    public Object getBean(String beanName) {
        if(!beanMap.containsKey(beanName)){
            log.info("[" + beanName + "]不存在");
        }
        return beanMap.get(beanName);
    }

    @Override
    public void close() throws IOException {
        Set<Map.Entry<String, BeanDefinition>> entries = bdMap.entrySet();
        for(Map.Entry<String, BeanDefinition>  entry: entries){
            BeanDefinition value = entry.getValue();
            String destoryMethodName = value.getBeanDestoryMethodName();
            try {
                Method method = value.getBeanClass().getMethod(destoryMethodName, null);
                method.invoke(value.getBeanClass(), null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

簡單測試一下:
實例bean:

public class User {

    private String name;

    private int age;

    //getter setter

    public void init(){
        System.out.println("init...");
    }

    public void destory(){
        System.out.println("destory...");
    }

}

工廠類:

public class TestFactory {
    public Object createMethod(){
        return new User();
    }

    public static Object staticCreateMethod(){
        return new User();
    }
}

測試類:

public class MySpringTest {

    static DefaultBeanFactory factory = new DefaultBeanFactory();

    @Test
    public void test() throws IllegalAccessException, InstantiationException {
        DefaultBeanDefinition bd = new DefaultBeanDefinition();
        bd.setClazz(User.class);
        bd.setSingleton(true);
        bd.setBeanFactoryName("TestFactory");
        bd.setCreateBeanMethodName("createMethod");
        bd.setStaticCreateBeanMethodName("staticCreateMethod");

        bd.setBeanInitMethodName("init");

        factory.register(bd, "user");

        System.out.println(factory.doGetBean("user"));
    }
}

小結

一個簡易的容器就這樣實現了,當然我們這里只是具備了基本的功能,實際上還差的遠,比如帶參數的bean的實例化等功能。但是IOC的基本原理已經表達出來了,后面我們只需在這個基礎上添加新的功能即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM