1. 程式人生 > >從零開始實現spring(一)最簡單的IOC

從零開始實現spring(一)最簡單的IOC

        本文原始碼可至碼雲查閱(https://gitee.com/wondersWX/myspring),我也還在一步步構建中,歡迎fork,歡迎star!

        自開啟始JavaWeb程式設計以來,從最初的jsp+servelt,到SSH框架,更行技術框架SSM框架,實現分散式架構,進行前後分離,接觸到的框架技術可以說數不勝數,但從沒能離開的就是spring,最開始的時候只是將spring用作物件容器,做框架整合,事務管理,然後慢慢從struts過渡到springmvc,使用spring進行view層的控制,再後來,使用spring-boot,有直接打包好的檢視層、和jpa、mybatise實體層,到現在使用spring-clouder實現微服務,通過路由加nigix實現前後分離架構。最近在一個微信群中,看到群友分享的一個面試題,如果不能使用spring,你會怎麼程式設計?發現自己看到這個問題著實愣了一下,的確使用spring已成了習慣,沒想過缺少了這個框架該怎麼辦。其實使用了spring這麼久,spring兩大基礎特性ioc(控制反轉),aop(面向切面)兩大特性可以說是十分熟悉了,自己對該如何實現這兩大特性也有一定的思路,那還等什麼,自己來嘗試實現一下吧!

        那麼就從大家最熟悉的開始,對於applicationContext.xml檔案中bean的配置可以說用過spring的人沒有不熟悉的,那麼其本質是什麼呢?簡單說,就是從xml檔案中讀取到bean的定義,spring的bean工廠基於此定義進行bean的建立和裝配,然後你就能輕鬆通過getBean()方法獲取到例項化的物件,再進一步抽象,就是將一段字串轉化為一個物件例項,熟悉反射的人可能就能想到了,這不挺像類的反射嘛,給出類路徑獲取Class,再使用newInstance獲取物件。沒錯spring的物件建立就是大量使用了反射的方式。好了說了這麼多我們就來嘗試自己實現一個最簡單的IOC吧!

        spring的BeanFactory在建立物件時並不是直接讀取xml檔案中的配置,而是對bean的定義進行了封裝也就是BeanDefinition

package com.fxx.bean;

public class BeanDefiniton {
    private String beanClassName;
    private Class beanClass;
    //bean的建立與裝載應由工廠完成,beanDefinition只維護此屬性,不提供建立bean的方法
    private Object bean;
    private PropertyValues pvs;

    public String getBeanClassName() {
        return beanClassName;
    }

    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
        //一般通過class反射建立例項,但ioc目的是通過修改字串達到調整例項的目標故在設定classname的同時,修改beanClass屬性
        try {
            this.beanClass = Class.forName(beanClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }

    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public PropertyValues getPvs() {
        return pvs;
    }

    public void setPvs(PropertyValues pvs) {
        this.pvs = pvs;
    }
}
    只是通過反射建立物件還不夠,我們當然想要給物件進行屬性注入,spring對屬性配置也進行了封裝
package com.fxx.bean;

public class PropertyValue {
    private final String name;
    private final Object value;
    //物件屬性屬於不可改變的時候,可將其設定為final,在構造器中賦值,並不提供set方法(提供也沒用)
    public  PropertyValue(String name,Object value){
        this.name=name;
        this.value=value;
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }
}
存在多個屬性注入,首相想到的是應該是使用list將propertyvalue管理起來,事實上spring也是這麼做的,但是為了更靈活的屬性注入,提供屬性注入前後的操作,那麼我們最熟悉的代理設計模式就可以用上了
package com.fxx.bean;

import java.util.ArrayList;
import java.util.List;

/**
 * 用於載入屬性前判斷
 */
public class PropertyValues {
    private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();

    public void addValue(PropertyValue pv){
        //此處進行注入前操作
        propertyValueList.add(pv);
    }

    public List<PropertyValue> getPropertyValueList() {
        return propertyValueList;
    }
}

好了有了beanDfiniton,現在只要將其註冊到工廠中,就可以獲取到物件了,我們來建立一個最簡單的物件工廠,使用map來管理beanDfiniton,通過物件name獲取物件,程式碼如下
package com.fxx.bean.factory;

import com.fxx.bean.BeanDefiniton;

public interface BeanFactory {

    Object getBean(String name) throws Exception;
    void registBeanDefinition(String name, BeanDefiniton beanDefiniton);
}
package com.fxx.bean.factory;

import com.fxx.bean.BeanDefiniton;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class AbstractBeanFactory implements BeanFactory{
    private final Map<String,BeanDefiniton> beanDefinitonMap = new ConcurrentHashMap<String, BeanDefiniton>();

    public Object getBean(String name) throws Exception {
        if(!beanDefinitonMap.containsKey(name)||beanDefinitonMap.get(name)==null){
            throw new Exception("物件定義不存在!");
        }
        return beanDefinitonMap.get(name).getBean();
    }

    /**
     * 註冊同時建立bean並裝載,將bean的建立與裝載相分離,建立的方式由子類實現(bean的建立有多種型別的需求)
     * @param name
     * @param beanDefiniton
     */
    public void registBeanDefinition(String name, BeanDefiniton beanDefiniton){
        if(beanDefiniton==null){
            return;
        }
        Object bean = null;
        try {
            bean = creatBean(beanDefiniton);
        } catch (Exception e) {
            e.printStackTrace();
        }
        beanDefiniton.setBean(bean);
        beanDefinitonMap.put(name,beanDefiniton);
    }

    protected abstract Object creatBean(BeanDefiniton beanDefiniton) throws Exception;
}

package com.fxx.bean.factory;

import com.fxx.bean.BeanDefiniton;
import com.fxx.bean.PropertyValue;
import com.fxx.bean.PropertyValues;

import java.lang.reflect.Field;

public class AutoWiredBeanFactory extends AbstractBeanFactory{

    /**
     * 實現例項建立以及屬性裝載
     * @param beanDefiniton
     * @return
     */
    @Override
    protected Object creatBean(BeanDefiniton beanDefiniton)  throws Exception{
        return doCreatBean(beanDefiniton);
    }

   private Object doCreatBean(BeanDefiniton beanDefiniton) throws Exception{
       Object bean = beanDefiniton.getBeanClass().newInstance();
       setPropertyValues(bean,beanDefiniton.getPvs());
       return bean;
   }

   private void setPropertyValues(Object bean,PropertyValues pvs) throws Exception {
        if(pvs.getPropertyValueList().size()==0){
            return;
        }
        for (PropertyValue pv : pvs.getPropertyValueList()){
            Field declaredField = bean.getClass().getDeclaredField(pv.getName());
            declaredField.setAccessible(true);
            declaredField.set(bean,pv.getValue());
        }
   }
}