手寫Spring---IOC容器(1)

IOC的分析:

 IOC---控制反轉,也稱依賴倒置。

如何去理解控制反轉呢?

反轉:依賴對象的得到被反轉,變爲由本身建立,反轉爲從IOC容器中獲取。   
複製代碼

帶來的好處:

1.代碼更爲簡潔,不須要再去new須要的對象   
    2.面向接口編程,使用類和具體類解耦,易擴展,替換實現者    
    3.方便進行AOP加強(沒有IOC就沒法AOP)          
複製代碼

IOC容器作什麼工做?

負責建立,管理類實例,向使用者提供實例
複製代碼

IOC容器就是工廠模式的實例,IOC容器也被稱爲Bean工廠


IOC設計實現

IOC容器的工做:建立和管理Bean,它是一個工廠,負責對外提供Bean實例
複製代碼

 Bean:組件,類的對象

Q1:它應該具有什麼行爲(對外接口)?

A:對外提供Bean實例,getBean()方法
複製代碼

Q2:這個getBean()方法是否須要參數?須要幾個,又爲何類型?

A:簡單工廠模式中,當工廠能建立不少類產品,若是須要某類產品,須要告訴工廠
複製代碼

Q3:這個getBean()方法的返回值的類型?

A:各類類型的bean只能爲Object<br><br>
複製代碼

此時咱們能夠編出BeanFactory接口

public interface BeanFactory { Object getBean(String name) throws Exception;}
複製代碼

Bean工廠如何知道如何建立Bean?

就是一個定義註冊,咱們能夠給它定義一個定義註冊接口,讓bean定義傳入bean工廠,告知工廠建立何種類型的Bean
複製代碼

Bean定義註冊接口

public interface BeanDefinitionRegistry {}
複製代碼

Q1:bean定義註冊接口中應定義些什麼方法?

註冊,獲取bean定義
複製代碼

Q2:註冊的bean定義信息如何區分?

每一個Bean要有一個獨立的名稱
複製代碼

此時咱們能夠編出BeanDefinitionRegistry接口

public interface BeanDefinitionRegistry {
    /**
    * 註冊Bean
    * @param beanName
    * @param beanDefinition
    * @throws Exception
    */
    void registerBeanDefinition(String beanName,BeanDefinition beanDefinition) throws Exception;

    /**
    * 獲取Bean
    * @param beanName
    * @return
    */
    BeanDefinition getBeanDefinition(String beanName);

    /**
    * 判斷Bean是否已經被註冊
    * @param beanName
    * @return
    */
    Boolean containsBeanDefinition(String beanName);
}
複製代碼


bean定義

Q1:bean定義的用途是什麼?

告訴bean工廠該如何建立某類bean
複製代碼

Q2:得到類的實例的方式有哪些?

1.new 構造方法
    2.工廠方法(靜態,成員方法)
複製代碼

Q3:bean工廠幫咱們建立bean時須要獲取哪些信息?

1.new 構造方法(須要知道類名)
    2.靜態工廠方法(須要知道工廠類名,工廠方法名)
    3.成員工廠方法(須要知道工廠類名--->工廠bean名,工廠方法名)
複製代碼

Q4:每次從bean工廠獲取bean實例時是否都要建立新的實例

否,由於有須要單例的狀況
複製代碼

Q5:bean定義是給bean工廠建立bean用的,那bean定義接口應向bean工廠提供哪些方法?

1.獲取bean的類名:getBeanClass()
    2.獲取工廠方法名:getFactoryMethodName()
    3.獲取工廠bean名:getFactoryBeanName()
    4.是不是單例:getScope(){isSingleton();  isPrototype();}
複製代碼

Q6:類對象交給IOC容器來管理,類對象的生命週期中還可能有什麼生命週期階段事情要作嗎?

建立後可能須要的初始化
    銷燬時有可能出現的某些銷燬邏輯(好比釋放資源)
    在bean定義提供讓用戶定製的初始化和銷燬方法便可(getInitMethodName(),getDestroyMethodName())
複製代碼

此時可寫出bean定義接口代碼:

public interface BeanDefinition {
        String SCOPE_SINGLETON = "singleton";
        String SCOPE_PROTOTYPE = "prototype";

        Class<?> getBeanClass();
        String getScope();
        boolean isSingleton();
        boolean isPrototype();
        String getFactoryBeanName();
        String getFactoryMethodName();
        String getInitMethodName();
        String getDestoryMethodName();
    
        //tips:java8開始就能夠直接寫接口默認方法了
        default boolean validate(){
            //class沒指定,工廠bean或工廠method不指定皆爲不合法狀況
            if (this.getBeanClass()==null){
                if(StringUtils.isBlank(getFactoryBeanName())||StringUtils.isBlank(getFactoryMethodName())){
                    return false;
                }
            }
    
            //class和工廠bean同時存在
            if (this.getBeanClass()!=null && StringUtils.isNotBlank(getFactoryBeanName())){
                return false;
            }
            return true;
        }
    }       
複製代碼

接口有了,如今咱們來編寫一個通用的bean定義(這裏使用lombok插件)

import lombok.Data;
    import org.apache.commons.lang.StringUtils;

    @Data
    public class GeneralBeanDefinition implements BeanDefinition{
        private Class<?> beanClass;
        private String scope = BeanDefinition.SCOPE_SINGLETON;
        private String factoryBeanName;
        private String factoryMethodName;
        private String initMethodName;
        private String destroyMethodName;
    
        public void setScope(String scope) {
            if (StringUtils.isNotBlank(scope)){
                this.scope = scope;
            }
        }
    
        @Override
        public Class<?> getBeanClass() {
            return this.beanClass;
        }
    
        @Override
        public String getScope() {
            return this.scope;
        }
    
        @Override
        public boolean isSingleton() {
            return BeanDefinition.SCOPE_SINGLETON.equals(this.scope);
        }
    
        @Override
        public boolean isPrototype() {
            return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope);
        }
    
        @Override
        public String getFactoryBeanName() {
            return factoryBeanName;
        }
    
        @Override
        public String getFactoryMethodName() {
            return factoryMethodName;
        }
    
        @Override
        public String getInitMethodName() {
            return this.initMethodName;
        }
    
        @Override
        public String getDestoryMethodName() {
            return this.destroyMethodName;
        }
    }
複製代碼

接下來該實現一個最基礎的DefaultBeanFactory讓它初步能工做起來

1.實現定義信息註冊

Q1:bean定義信息如何存放?

A:Map
複製代碼

Q2:bean定義是否能夠重名,重名時如何解決?

A:直接設計爲不能重名
複製代碼

2.實現bean工廠

Q1:建立的bean使用什麼存放,方便下次獲取?

Map
複製代碼

Q2:在getBean方法中須要作什麼事情

建立bean實例,進行初始化
複製代碼

知道這些以後,此時咱們簡單完成一個DefaultBeanFactory

public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegistry, Closeable {

    //common-logging包和log4j-api包配合便可
    private final Log logger = LogFactory.getLog(getClass());

    //考慮併發狀況,256個前不須要進行擴容
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

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

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws Exception {
        //參數檢查
        Objects.requireNonNull(beanName,"註冊bean須要輸入beanName");
        Objects.requireNonNull(beanDefinition,"註冊bean須要輸入beanDefinition");

        //檢驗給入的bean是否合法
        if (!beanDefinition.validate()){
            throw new Exception("名字爲["+beanName+"]的bean定義不合法,"+beanDefinition);
        }

        if (this.containsBeanDefinition(beanName)){
            throw new Exception("名字爲["+beanName+"]的bean定義已經存在,"+this.getBeanDefinition(beanName));
        }

        this.beanDefinitionMap.put(beanName,beanDefinition);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return this.beanDefinitionMap.get(beanName);
    }

    @Override
    public Boolean containsBeanDefinition(String beanName) {
        return this.beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public Object getBean(String name) throws Exception {
        return this.doGetBean(name);
    }

    //不須要判斷scope,由於只有單例bean才須要放入map中
    //使用protected保證只有DefaultBeanFactory的子類能夠調用該方法
    protected Object doGetBean(String beanName) throws Exception{
        Objects.requireNonNull(beanName,"beanName不能爲空");
        Object instance = beanMap.get(beanName);

        if (instance != null){
            return instance;
        }
        BeanDefinition beanDefinition = this.getBeanDefinition(beanName);
        Objects.requireNonNull(beanDefinition,"beanDefinition不能爲空");

        Class<?> type = beanDefinition.getBeanClass();

        //由於總共就只有3種方式,也不須要擴充或者是修改代碼了,因此就不須要考慮使用策略模式了
        if (type != null){
            if (StringUtils.isBlank(beanDefinition.getFactoryMethodName())){
                instance = this.createInstanceByConstructor(beanDefinition);
            } else {
                instance = this.createInstanceByStaticFactoryMethod(beanDefinition);
            }
        }else {
            instance = this.createInstanceByFactoryBean(beanDefinition);
        }

        this.doInit(beanDefinition,instance);

        if (beanDefinition.isSingleton()){
            beanMap.put(beanName,instance);
        }

        return instance;
    }

    //構造方法來建立對象
    private Object createInstanceByConstructor(BeanDefinition beanDefinition) throws IllegalAccessException, InstantiationException {
        try{
            return beanDefinition.getBeanClass().newInstance();
        } catch (SecurityException e){
            logger.error("建立bean的實例異常,beanDefinition"+beanDefinition,e);
            throw e;
        }
    }

    //靜態工廠方法(暫時不考慮帶參數)
    private Object createInstanceByStaticFactoryMethod(BeanDefinition beanDefinition) throws Exception{
        Class<?> type = beanDefinition.getBeanClass();
        Method method = type.getMethod(beanDefinition.getFactoryMethodName(),null);
        return method.invoke(type,null);
    }

    //工廠bean方法來建立對象(暫時不考慮帶參數)
    private Object createInstanceByFactoryBean(BeanDefinition beanDefinition) throws Exception{
        Object factoryBean = this.doGetBean(beanDefinition.getFactoryBeanName());
        Method method = factoryBean.getClass().getMethod(beanDefinition.getFactoryMethodName(),null);
        return method.invoke(factoryBean,null);
    }

    //初始化方法
    private void doInit(BeanDefinition beanDefinition, Object instance) throws Exception{
        if (StringUtils.isNotBlank(beanDefinition.getInitMethodName())){
            Method method = instance.getClass().getMethod(beanDefinition.getInitMethodName(),null);
            method.invoke(instance,null);
        }
    }
    @Override
    public void close() throws IOException {
        //執行單例實例的銷燬方法
        //遍歷map把bean都取出來而後調用每一個bean的銷燬方法
        for (Map.Entry<String,BeanDefinition> entry : this.beanDefinitionMap.entrySet()){
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();

            if (beanDefinition.isSingleton() && StringUtils.isNotBlank(beanDefinition.getDestoryMethodName())){
                Object instance = this.beanMap.get(beanName);
                try {
                    Method method = instance.getClass().getMethod(beanDefinition.getDestoryMethodName(),null);
                    method.invoke(instance,null);
                }catch (NoSuchMethodException|SecurityException|IllegalAccessException|IllegalArgumentException|InvocationTargetException e){
                    logger.error("執行bean["+beanName+"] "+beanDefinition+"的銷燬方法異常",e);
                }
            }
        }
    }
}
複製代碼

tips:此時單例的線程安全還沒法保證!!!

擴展DefaultBeanFactory

Thinking:對於單例bean咱們是否能夠提早實例化,這有什麼好處?

A:能夠提早實例化,空間換時間的方法,啓動慢使用快併線程安全
複製代碼

若是要實現提早實例化單例bean的功能,代碼以下

public class PreBuildBeanFactory extends DefaultBeanFactory{

    private Log logger = LogFactory.getLog(getClass());

    private List<String> beanNames = new ArrayList<>();

    @Override
    public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition) throws Exception{
        super.registerBeanDefinition(beanName,beanDefinition);
        synchronized (beanNames){
            beanNames.add(beanName);
        }
    }

    //使用synchronized解決線程安全問題
    public void preInstantiateSingletons() throws Exception{
        synchronized (beanNames){
            for (String name : beanNames){
                BeanDefinition beanDefinition = this.getBeanDefinition(name);
                if (beanDefinition.isSingleton()){
                    this.doGetBean(name);
                    if (logger.isDebugEnabled()){
                        logger.debug("preInstantiate:name="+name+" "+beanDefinition);
                    }
                }
            }
        }
    }
}
複製代碼

此時咱們已經初步完成了一個IOC容器,能夠來個簡單的測試

編寫一個bean1和一個bean1的工廠,還有一個test方法

Bean1.java

public class Bean1 {

    public void doSomething(){
        System.out.println(System.currentTimeMillis()+" "+this);
    }

    public void init(){
        System.out.println("bean1的init已執行");
    }

    public void destroy(){
        System.out.println("bean1的destroy已執行");
    }
}
複製代碼

Bean1Factory.javajava

public class Bean1Factory {

    public static Bean1 getBean1(){
        return new Bean1();
    }

    public Bean1 getOtherBean1(){
        return new Bean1();
    }
}
複製代碼

測試用例DefaultBeanFactoryTestapache

public class DefaultBeanFactoryTest {

    static DefaultBeanFactory defaultBeanFactory = new DefaultBeanFactory();

    @Test
    public void testRegist() throws Exception{
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setBeanClass(Bean1.class);
        generalBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        generalBeanDefinition.setInitMethodName("init");
        generalBeanDefinition.setDestroyMethodName("destroy");

        defaultBeanFactory.registerBeanDefinition("bean1",generalBeanDefinition);
    }

    @Test
    public void testRegistStaticFactoryMethod() throws Exception{
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setBeanClass(Bean1Factory.class);
        generalBeanDefinition.setFactoryMethodName("getBean1");
        defaultBeanFactory.registerBeanDefinition("staticBean1",generalBeanDefinition);
    }

    @Test
    public void testRegistFactoryMethod() throws Exception{
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setBeanClass(Bean1Factory.class);
        String factoryBeanName = "factory";
        defaultBeanFactory.registerBeanDefinition(factoryBeanName,generalBeanDefinition);

        generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setFactoryBeanName(factoryBeanName);
        generalBeanDefinition.setFactoryMethodName("getOtherBean1");
        generalBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        defaultBeanFactory.registerBeanDefinition("factoryBean",generalBeanDefinition);
    }

    @AfterClass
    public static void testGetBean() throws Exception{
        System.out.println("構造方法方式···");
        for (int i = 0;i<3;i++){
            Bean1 bean1 = (Bean1) defaultBeanFactory.getBean("bean1");
            bean1.doSomething();
        }

        System.out.println("靜態工廠方法方式···");
        for (int i = 0;i<3;i++){
            Bean1 bean1 = (Bean1) defaultBeanFactory.getBean("staticBean1");
            bean1.doSomething();
        }

        System.out.println("工廠方法方式···");
        for (int i = 0;i<3;i++){
            Bean1 bean1 = (Bean1) defaultBeanFactory.getBean("factoryBean");
            bean1.doSomething();
        }

        defaultBeanFactory.close();
    }
}
複製代碼

此測試用例的運行結果爲(注意工廠方法時咱們設置了多例,因此bean應該每一個都不一樣):

構造方法方式···
bean1的init已執行
1555370012087 MySpring.Bean1@5e8c92f4
1555370012088 MySpring.Bean1@5e8c92f4
1555370012088 MySpring.Bean1@5e8c92f4
靜態工廠方法方式···
1555370012088 MySpring.Bean1@50134894
1555370012088 MySpring.Bean1@50134894
1555370012088 MySpring.Bean1@50134894
工廠方法方式···
1555370012089 MySpring.Bean1@2957fcb0
1555370012089 MySpring.Bean1@1376c05c
1555370012090 MySpring.Bean1@51521cc1
bean1的destroy已執行複製代碼
相關文章
相關標籤/搜索