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

寧願。發表於2018-12-13

目錄

導語

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的基本原理已經表達出來了,後面我們只需在這個基礎上新增新的功能即可。

相關文章