框架源碼系列二:手寫Spring-IOC和Spring-DI(IOC分析、IOC設計實現、DI分析、DI實現)

1、IOC分析

1. IOC是什麼?

IOC:Inversion of Control控制反轉,也稱依賴倒置(反轉)java

問題:如何理解控制反轉?git

反轉:依賴對象的得到被反轉了。由本身建立,反轉爲從IOC容器中獲取(和自動注入)github

2. IOC容器帶來什麼好處?

  1)代碼更簡潔,不須要去new須要使用的對象了。spring

  2)面向接口編程,使用者與具體類解耦,易擴展、替換實現者。apache

  3)能夠方便進行AOP加強。進行AOP的前提是有IOC編程

3. IOC容器作什麼工做?

  IOC容器的工做:負責建立、管理類實例,向使用者提供實例。數組

4. IOC容器是不是工廠模式的實例?

  是的。IOC容器負責來建立類實例對象,使用者須要實例就從IOC容器中get。也稱IOC容器爲bean工廠。緩存

2、IOC設計實現

1. IOC容器的工做

建立、管理bean。它是一個工廠,負責對外提供bean實例。

問:bean是什麼?安全

  bean是組件,就是類對象!ide

1)IOC容器應該具有什麼行爲(對外接口)?

  對外提供bean實例,getBean()方法

2) 這個getBean()方法是否須要參數?須要幾個參數、什麼類型的參數?

  簡單工廠模式中,當工廠能建立不少類產品時,如要建立某類產品,須要告訴工廠。

3)這個getBean()方法的返回值應是什麼類型?

  各類類型的bean,那就只能是Object了。

通過上面的問題Bean工廠的接口就能夠定義出來了!!!

2. Bean工廠接口:

Bean工廠代碼:

package com.study.spring.beans;

/**
 * 
 * @Description: IOC容器(bean工廠)接口:負責建立bean實例
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public interface BeanFactory {
    /**
     * 獲取bean
     * 
     * @param name bean的名字
     *            
     * @return bean 實例
     * @throws Exception
     */
    Object getBean(String name) throws Exception;
}

 Bean工廠怎麼知道該如何建立bean?

如何告訴Bean工廠?

  就是一個定義註冊,咱們能夠給它定義一個bean定義註冊接口

3. Bean定義註冊接口

 

Bean定義註冊接口代碼:

package com.study.spring.beans;

/**
 * 
 * @Description: bean定義BeanDefinition定義好了就須要告訴IOC容器(bean工廠):
 * 經過bean定義註冊接口BeanDefinitionRegistry把bean定義BeanDefinition註冊到IOC容器(bean工廠)BeanFactory
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public interface BeanDefinitionRegistry {

    //註冊bean定義
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException;

    //獲取bean定義
    BeanDefinition getBeanDefinition(String beanName);

    //判斷是否包含bean定義
    boolean containsBeanDefinition(String beanName);

}

4. Bean定義

問題1:bean定義的用途是什麼?

告訴Bean工廠該如何建立某類bean

問題2:得到類的實例的方式有哪些?

 new 構造方法

Person p = new Person();

工廠方法

靜態的

public class PersonFactory {
    public static Person getPerson() {
        return new Person();
    }
}

成員方法

public class PersonFactory {
    public Person getPerson() {
        return new Person();
    }
}

問題3:Bean工廠幫咱們建立bean時,它須要獲知哪些信息?

1)new 構造方法的方式建立bean時,Bean工廠須要知道要建立的類的類名

2)靜態工廠方法的方式建立bean時,Bean工廠須要知道工廠類名、工廠方法名

3)成員工廠方法的方式建立bean時,Bean工廠須要知道工廠類名(工廠bean名)、工廠方法名

  由於須要獲取工廠類對象去調用工廠方法名建立bean,因此直接給工廠bean名先建立工廠bean對象

問題4:每次從Bean工廠獲取bean實例時,是否都須要建立一個新的?

  否,有的只須要單實例

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

new 構造方法的方式建立bean時,須要告訴bean工廠怎麼獲取類的名稱——獲取bean的類名:getBeanClass (): Class

靜態工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠方法名:getFactoryMethodName() : String

成員工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠bean名:getFactoryBeanName() : String

是不是單例等方法:getScope() : Sting、isSingleton()、isPrototype()

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

好比建立對象後可能須要進行一些初始化

還有一些對象在銷燬時可能要進行一些特定的銷燬邏輯(如釋放資源)

那就在Bean定義中提供讓用戶可指定初始化、銷燬方法。

對Bean工廠就需提供getInitMethodName()、getDestroyMethodName()

5. Bean定義接口

 

bean定義接口BeanDefinition代碼:

package com.study.spring.beans;

import org.apache.commons.lang3.StringUtils;

/**
 * 
 * @Description: bean定義接口:要IOC容器(bean工廠)建立bean實例,就得告訴IOC容器(bean工廠)須要建立什麼樣的bean-BeanDefinition
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public interface BeanDefinition {

    String SCOPE_SINGLETION = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 類:new 構造方法的方式建立bean時,須要告訴bean工廠怎麼獲取類的名稱
     */
    Class<?> getBeanClass();

    /**
     * Scope
     */
    String getScope();

    /**
     * 是否單例
     */
    boolean isSingleton();

    /**
     * 是否原型
     */
    boolean isPrototype();

    /**
     * 工廠bean名:成員工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠bean名
     */
    String getFactoryBeanName();

    /**
     * 工廠方法名:靜態工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠方法名
     */
    String getFactoryMethodName();

    /**
     * 初始化方法
     */
    String getInitMethodName();

    /**
     * 銷燬方法
     */
    String getDestroyMethodName();

    /**
     * 校驗bean定義的合法性
     */
    default boolean validate() {
        // 沒定義class,工廠bean或工廠方法沒指定,則不合法。
        if (this.getBeanClass() == null) {
            if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) {
                return false;
            }
        }

        // 定義了類,又定義工廠bean,不合法
        if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) {
            return false;
        }

        return true;
    }

}

 實現bean定義接口BeanDefinition實現一個通用的bean定義GenericBeanDefinition

package com.study.spring.beans;

import org.apache.commons.lang3.StringUtils;

/**
 * 
 * @Description: bean定義有了就須要實現bean定義接口BeanDefinition實現一個通用的bean
 * 定義GenericBeanDefinition,用來承載數據
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public class GenericBeanDefinition implements BeanDefinition {

    //bean的名稱
    private Class<?> beanClass;

    //scope 默認單例
    private String scope = BeanDefinition.SCOPE_SINGLETION;

    //工廠bean名
    private String factoryBeanName;

    //工廠方法名
    private String factoryMethodName;

    //初始化方法
    private String initMethodName;

    //銷燬方法
    private String destroyMethodName;

    //設置bean的名稱
    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    //設置scope
    public void setScope(String scope) {
        if (StringUtils.isNotBlank(scope)) {
            this.scope = scope;
        }
    }

    //設置工廠bean名
    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }

    //設置工廠方法名
    public void setFactoryMethodName(String factoryMethodName) {
        this.factoryMethodName = factoryMethodName;
    }

    //設置初始化方法
    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    //設置銷燬方法
    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }

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

    @Override
    public String getScope() {
        return this.scope;
    }

    @Override
    public boolean isSingleton() {
        return BeanDefinition.SCOPE_SINGLETION.equals(this.scope);
    }

    @Override
    public boolean isPrototype() {
        return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope);
    }

    @Override
    public String getFactoryBeanName() {
        return this.factoryBeanName;
    }

    @Override
    public String getFactoryMethodName() {
        return this.factoryMethodName;
    }

    @Override
    public String getInitMethodName() {
        return this.initMethodName;
    }

    @Override
    public String getDestroyMethodName() {
        return this.destroyMethodName;
    }

    @Override
    public String toString() {
        return "GenericBeanDefinition [beanClass=" + beanClass + ", scope=" + scope + ", factoryBeanName="
                + factoryBeanName + ", factoryMethodName=" + factoryMethodName + ", initMethodName=" + initMethodName
                + ", destroyMethodName=" + destroyMethodName + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((beanClass == null) ? 0 : beanClass.hashCode());
        result = prime * result + ((destroyMethodName == null) ? 0 : destroyMethodName.hashCode());
        result = prime * result + ((factoryBeanName == null) ? 0 : factoryBeanName.hashCode());
        result = prime * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode());
        result = prime * result + ((initMethodName == null) ? 0 : initMethodName.hashCode());
        result = prime * result + ((scope == null) ? 0 : scope.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        GenericBeanDefinition other = (GenericBeanDefinition) obj;
        if (beanClass == null) {
            if (other.beanClass != null)
                return false;
        } else if (!beanClass.equals(other.beanClass))
            return false;
        if (destroyMethodName == null) {
            if (other.destroyMethodName != null)
                return false;
        } else if (!destroyMethodName.equals(other.destroyMethodName))
            return false;
        if (factoryBeanName == null) {
            if (other.factoryBeanName != null)
                return false;
        } else if (!factoryBeanName.equals(other.factoryBeanName))
            return false;
        if (factoryMethodName == null) {
            if (other.factoryMethodName != null)
                return false;
        } else if (!factoryMethodName.equals(other.factoryMethodName))
            return false;
        if (initMethodName == null) {
            if (other.initMethodName != null)
                return false;
        } else if (!initMethodName.equals(other.initMethodName))
            return false;
        if (scope == null) {
            if (other.scope != null)
                return false;
        } else if (!scope.equals(other.scope))
            return false;
        return true;
    }

}

 咱們繼續看下面的圖:

 說明:bean定義BeanDefinition經過bean定義註冊接口BeanDefinitionRegistry註冊到Bean工廠BeanFactory,Bean工廠BeanFactory負責建立bean

6. BeanFactory實現

 實現一個最基礎的默認bean工廠:DefaultBeanFactory

 

說明:

6.1 實現bean定義信息註冊接口

問題1:bean定義信息如何存放?

  Map

問題2:bean定義是否能夠重名?重名怎麼辦?

  重名拋異常

6.2 實現bean工廠

問題1:建立的bean用什麼存放,方便下次獲取?

  Map,由於getBean是經過名字來取的,放在Map中更好

問題2:在getBean方法中要作哪些事?

  建立bean實例,進行初始化

6.3 Bean工廠實現代碼:

package com.study.spring.beans;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * @Description: bean定義BeanDefinition有了,bean定義註冊BeanDefinitionRegistry也有了,
 * 就能夠實現一個默認的bean工廠DefaultBeanFactory來建立bean實例了,DefaultBeanFactory除了須要實現BeanFactory外,
 * 還須要實現Bean定義註冊接口BeanDefinitionRegistry,由於要把bean定義註冊到bean工廠裏面
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {

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

    //用Map來存放bean定義信息
    private Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256);

    //用Map來存放建立的bean實例,注意這裏只是存放單例bean,多實例每次都要建立新的,不須要存放
    private Map<String, Object> beanMap = new ConcurrentHashMap<>(256);

    //註冊bean定義
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionRegistException {
        //判斷給入的beanName和beanDefinition不能爲空
        Objects.requireNonNull(beanName, "註冊bean須要給入beanName");
        Objects.requireNonNull(beanDefinition, "註冊bean須要給入beanDefinition");

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

        //若是已存在bean定義就拋異常
        if (this.containsBeanDefinition(beanName)) {
            throw new BeanDefinitionRegistException(
                    "名字爲[" + beanName + "] 的bean定義已存在:" + this.getBeanDefinition(beanName));
        }

        //把bean定義放到Map裏面
        this.beanDefintionMap.put(beanName, beanDefinition);
    }

    //根據bean的名字從Map裏面獲取bean定義
    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return this.beanDefintionMap.get(beanName);
    }

    //根據bean的名字判斷Map裏面是否包含bean定義
    @Override
    public boolean containsBeanDefinition(String beanName) {

        return this.beanDefintionMap.containsKey(beanName);
    }

    //根據bean的名字獲取bean實例,裏面主要作的工做是建立bean實例和對bean實例進行初始化
    @Override
    public Object getBean(String name) throws Exception {
        return this.doGetBean(name);
    }

    //根據bean的名字獲取bean實例,裏面主要作的工做是建立bean實例和對bean實例進行初始化
    protected Object doGetBean(String beanName) throws Exception {
        //判斷給入的bean名字不能爲空
        Objects.requireNonNull(beanName, "beanName不能爲空");

        //先從beanMap裏面獲取bean實例
        Object instance = beanMap.get(beanName);

        //若是beanMap裏面已存在bean實例就直接返回,不須要走後面的流程了
        if (instance != null) {
            return instance;
        }

        //從beanDefintionMap裏面獲取bean定義信息
        BeanDefinition bd = this.getBeanDefinition(beanName);
        //bean定義信息不能爲空
        Objects.requireNonNull(bd, "beanDefinition不能爲空");

        //獲取bean的類型
        Class<?> type = bd.getBeanClass();
        if (type != null) {
            //若是bean的類型不爲空,而且工廠方法名爲空,說明是使用構造方法的方式來建立bean實例
            if (StringUtils.isBlank(bd.getFactoryMethodName())) {
                // 構造方法來構造對象
                instance = this.createInstanceByConstructor(bd);
            } 
            //若是bean的類型不爲空,而且工廠方法名不爲空,說明是使用靜態工廠方法的方式來建立bean實例
            else {
                // 靜態工廠方法
                instance = this.createInstanceByStaticFactoryMethod(bd);
            }
        } 
        //若是bean的類型爲空,說明是使用工廠bean的方式來建立bean實例
        else {
            // 工廠bean方式來構造對象
            instance = this.createInstanceByFactoryBean(bd);
        }

        // 執行初始化方法
        this.doInit(bd, instance);

        //存放單例的bean到beanMap
        if (bd.isSingleton()) {
            beanMap.put(beanName, instance);
        }

        return instance;
    }

    // 構造方法來構造對象 反射
    private Object createInstanceByConstructor(BeanDefinition bd)
            throws InstantiationException, IllegalAccessException {
        try {
            //拿到bean的類型,而後調用newInstance經過反射來建立bean實例
            return bd.getBeanClass().newInstance();
        } catch (SecurityException e1) {
            logger.error("建立bean的實例異常,beanDefinition:" + bd, e1);
            throw e1;
        }
    }

    // 靜態工廠方法 反射
    private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
        //拿到bean的類型
        Class<?> type = bd.getBeanClass();
        //經過靜態工廠方法方法的名字getFactoryMethodName反射出bean的方法m建立bean實例
        Method m = type.getMethod(bd.getFactoryMethodName(), null);
        return m.invoke(type, null);
    }

    // 工廠bean方式來構造對象 反射
    private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {

        //經過bean定義信息中工廠bean的名字獲取工廠bean的實例
        Object factoryBean = this.doGetBean(bd.getFactoryBeanName());

        //經過bean定義信息中工廠方法的名字反射出工廠bean的方法m建立bean實例
        Method m = factoryBean.getClass().getMethod(bd.getFactoryMethodName(), null);
        return m.invoke(factoryBean, null);
    }

    //執行初始化方法
    private void doInit(BeanDefinition bd, Object instance) throws Exception {
        // 獲取bean定義中的初始化方法,若是存在初始化方法就經過反射去執行初始化方法
        if (StringUtils.isNotBlank(bd.getInitMethodName())) {
            Method m = instance.getClass().getMethod(bd.getInitMethodName(), null);
            m.invoke(instance, null);
        }
    }

    //銷燬
    @Override
    public void close() throws IOException {
        // 執行單例實例的銷燬方法
        for (Entry<String, BeanDefinition> e : this.beanDefintionMap.entrySet()) {
            String beanName = e.getKey();
            BeanDefinition bd = e.getValue();

            //獲取bean定義中的銷燬方法,若是存在銷燬方法就經過反射去執行銷燬方法
            if (bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) {
                Object instance = this.beanMap.get(beanName);
                try {
                    Method m = instance.getClass().getMethod(bd.getDestroyMethodName(), null);
                    m.invoke(instance, null);
                } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
                        | InvocationTargetException e1) {
                    logger.error("執行bean[" + beanName + "] " + bd + " 的 銷燬方法異常!", e1);
                }
            }
        }
    }
}

 6.4 擴展DefaultBeanFactory

 對於單例bean,咱們能夠提早實例化,這樣作的好處是不用在須要的時候再取獲取了,能夠保證線程安全,提升性能

package com.study.spring.beans;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * @Description: 提早實例化單例bean
 * @author leeSmall
 * @date 2018年11月27日
 *
 */
public class PreBuildBeanFactory extends DefaultBeanFactory {

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

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

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

    public void preInstantiateSingletons() throws Exception {
        synchronized (beanNames) {
            for (String name : beanNames) {
                BeanDefinition bd = this.getBeanDefinition(name);
                if (bd.isSingleton()) {
                    this.doGetBean(name);
                    if (logger.isDebugEnabled()) {
                        logger.debug("preInstantiate: name=" + name + " " + bd);
                    }
                }
            }
        }
    }
}

6.5 測試IOC容器(bean工廠)建立bean實例

 須要建立的bean實例ABean

package com.study.spring.samples;

/**
 * 
 * @Description: 須要建立的bean實例ABean
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public class ABean {

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

    public void init() {
        System.out.println("ABean.init() 執行了");
    }

    public void destroy() {
        System.out.println("ABean.destroy() 執行了");
    }
}

 建立bean實例ABean的工廠ABeanFactory

package com.study.spring.samples;

/**
 * 
 * @Description: 建立bean實例ABean的工廠ABeanFactory
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public class ABeanFactory {

    //靜態工廠方式
    public static ABean getABean() {
        return new ABean();
    }

    //工廠bean的方式
    public ABean getABean2() {
        return new ABean();
    }
}

 測試IOC容器(bean工廠)建立bean實例ABean

package v1;

import org.junit.AfterClass;
import org.junit.Test;

import com.dn.spring.beans.BeanDefinition;
import com.dn.spring.beans.DefaultBeanFactory;
import com.dn.spring.beans.GenericBeanDefinition;
import com.dn.spring.samples.ABean;
import com.dn.spring.samples.ABeanFactory;

/**
 * 
 * @Description: 測試IOC容器(bean工廠)建立bean實例ABean
 * @author leeSmall
 * @date 2018年11月29日
 *
 */
public class DefaultBeanFactoryTest {

    static DefaultBeanFactory bf = new DefaultBeanFactory();

    //測試構造方法方式建立bean實例
    @Test
    public void testRegist() throws Exception {

        //建立bean定義
        GenericBeanDefinition bd = new GenericBeanDefinition();

        //設置bean的類名
        bd.setBeanClass(ABean.class);
        //設置是否單例
        bd.setScope(BeanDefinition.SCOPE_SINGLETION);
        // bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);

        //設置bean的初始化方法
        bd.setInitMethodName("init");
        //設置bean的銷燬方法
        bd.setDestroyMethodName("destroy");

        //把bean定義註冊到bean工廠DefaultBeanFactory bf
        bf.registerBeanDefinition("aBean", bd);

    }

    //靜態工廠方法的方式建立bean實例
    @Test
    public void testRegistStaticFactoryMethod() throws Exception {
        //建立bean定義
        GenericBeanDefinition bd = new GenericBeanDefinition();
        //設置工廠bean的名字
        bd.setBeanClass(ABeanFactory.class);
        //設置工廠方法名
        bd.setFactoryMethodName("getABean");
        //把bean定義註冊到bean工廠DefaultBeanFactory bf
        bf.registerBeanDefinition("staticAbean", bd);
    }

    //工廠bean的方式建立bean實例
    @Test
    public void testRegistFactoryMethod() throws Exception {
        //建立工廠bean定義
        GenericBeanDefinition bd = new GenericBeanDefinition();
        //設置工廠bean的名字
        bd.setBeanClass(ABeanFactory.class);
        String fbname = "factory";
        //把工廠bean註冊到bean工廠DefaultBeanFactory bf
        bf.registerBeanDefinition(fbname, bd);

        //建立bean定義
        bd = new GenericBeanDefinition();
        //設置工廠bean的名字
        bd.setFactoryBeanName(fbname);
        //設置工廠bean的方法名
        bd.setFactoryMethodName("getABean2");
        //設置是不是單列
        bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);

        //把bean定義註冊到bean工廠DefaultBeanFactory bf
        bf.registerBeanDefinition("factoryAbean", bd);
    }

    //獲取bean實例並調用裏面的方法
    @AfterClass
    public static void testGetBean() throws Exception {
        System.out.println("構造方法方式------------");
        for (int i = 0; i < 3; i++) {
            ABean ab = (ABean) bf.getBean("aBean");
            ab.doSomthing();
        }

        System.out.println("靜態工廠方法方式------------");
        for (int i = 0; i < 3; i++) {
            ABean ab = (ABean) bf.getBean("staticAbean");
            ab.doSomthing();
        }

        System.out.println("工廠方法方式------------");
        for (int i = 0; i < 3; i++) {
            ABean ab = (ABean) bf.getBean("factoryAbean");
            ab.doSomthing();
        }

        //銷燬
        bf.close();
    }
}

輸出結果:

構造方法方式------------
ABean.init() 執行了
1543478493599 com.dn.spring.samples.ABean@46fbb2c1
1543478493599 com.dn.spring.samples.ABean@46fbb2c1
1543478493599 com.dn.spring.samples.ABean@46fbb2c1
靜態工廠方法方式------------
1543478493599 com.dn.spring.samples.ABean@5ef04b5
1543478493599 com.dn.spring.samples.ABean@5ef04b5
1543478493599 com.dn.spring.samples.ABean@5ef04b5
工廠方法方式------------
1543478493599 com.dn.spring.samples.ABean@5f4da5c3
1543478493600 com.dn.spring.samples.ABean@443b7951
1543478493600 com.dn.spring.samples.ABean@14514713
ABean.destroy() 執行了

3、DI分析

DI(Dependency Injection)依賴注入分析

問題1:哪些地方會有依賴?

  構造參數依賴

  屬性依賴

問題2:依賴注入的本質是什麼?

  賦值,給入構造參數值,給屬性賦值

問題3:參數值、屬性值多是什麼值?

  直接值、bean依賴

舉例:

public class Girl{
    public Girl(String name,int age,char cup,Boy boyfriend){
    }  

}

name,age,cup都是直接值,boyfriend是bean依賴

問題4:直接值會有哪幾種狀況?

  基本數據類型、String

  數組、集合

  Properties

  Map

本質:參數值、屬性值都是值。bean工廠在進行依賴注入時,就是給入值。

4、DI實現

1. 構造參數依賴

 1.1 構造參數依賴定義分析

public class Girl{
    public Girl(String name,int age,char cup,Boy boyfriend){
    }  

}

問題1:咱們要建立一個Girl是如何建立的?

Boy  leeSmall = new Boy("leeSmall");
Girl girl = new Girl("小青",18,'D',leeSmall);

直接把值傳入構造函數便可

問題2:咱們可不能夠這樣來定義構造參數依賴?

  第一個參數值是:"小青"

  第二個參數值是:18

  第三個參數值是:'D'

  第四個參數值是:依賴一個Boy Bean

徹底能夠!

構造參數依賴設計

問題1:參數能夠有多個,用什麼存儲?

  集合:List

問題2:參數有順序,如何體現順序?

  按參數順序放入List

問題3:參數能夠值直接值,也能夠是bean依賴,如何表示?

  由於能夠有多種值,那就只能用Object

  List<Object>  constructorArgumentValues

問題4:若是用Object來表示值,如何區分是Bean依賴?

  爲Bean依賴定義一種數據類型BeanReference,bean工廠在構造Bean實例時,遍歷判斷參數是不是BeanReference,若是是則替換爲依賴的bean實例。

問題5:若是直接值是數組、集合等,它們的元素中有的是bean依賴,怎麼處理?

  元素值仍是用BeanReference,一樣bean工廠在使用時需遍歷替換。

1.2 BeanReference

BeanReference就是用來講明bean依賴的:依賴哪一個Bean

package com.dn.spring.beans;
/**
 * 用於依賴注入中描述bean依賴
 */
public class BeanReference {
    private String beanName;

    public BeanReference(String beanName) {
        super();
        this.beanName = beanName;
    }

    /**
     * 得到引用的beanName
     */
    public String getBeanName() {
        return this.beanName;
    }
}

1.3 在BeanDefinition中增長得到構造參數值的方法

List<?> getConstructorArgumentValues();

在GenericBeanDefinition中增長對應的實現

    //構造參數存放屬性
    private List<?> constructorArgumentValues;

    //得到構造參數值
    public List<?> getConstructorArgumentValues() {
        return constructorArgumentValues;
    }

    //設置構造參數值
    public void setConstructorArgumentValues(List<?> constructorArgumentValues) {
        this.constructorArgumentValues = constructorArgumentValues;
    }

 構造參數依賴有了,下面就能夠來實現構造參數依賴注入了!

1.4 BeanFactory中實現構造參數依賴注入

1.4.1 首先須要把bean定義中的構造參數引用轉爲真實的值,在DefaultBeanFactory中增長一個方法來幹這事。

代碼實現:

    //把bean定義中的構造參數引用轉爲真實的值
    private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {

        return this.getRealValues(bd.getConstructorArgumentValues());

    }

    //把bean定義中的構造參數引用轉爲真實的值
    private Object[] getRealValues(List<?> defs) throws Exception {
        if (CollectionUtils.isEmpty(defs)) {
            return null;
        }

        Object[] values = new Object[defs.size()];
        int i = 0;
        Object v = null;
        for (Object rv : defs) {
            if (rv == null) {
                v = null;
            } else if (rv instanceof BeanReference) {
                v = this.doGetBean(((BeanReference) rv).getBeanName());
            } else if (rv instanceof Object[]) {
                // TODO 處理集合中的bean引用
            } else if (rv instanceof Collection) {
                // TODO 處理集合中的bean引用
            } else if (rv instanceof Properties) {
                // TODO 處理properties中的bean引用
            } else if (rv instanceof Map) {
                // TODO 處理Map中的bean引用
            } else {
                v = rv;
            }

            values[i++] = v;
        }

        return values;
    }

問題:有參數了,如何判定是哪一個構造方法、哪一個工廠方法?須要考慮下面的狀況

a)方法是能夠重載的

b)形參定義時多是接口或者父類,實參則是具體的子實現

c)能夠經過反射獲取提供的構造方法、方法,以下:

java.lang.Class

反射獲取構造方法:

   //獲取全部構造方法
   @CallerSensitive
    public Constructor<?>[] getConstructors() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }
    //獲取指定構造方法
    @CallerSensitive
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }

 反射獲取方法:

    //獲取全部方法
    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }
    //獲取指定方法
    @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

判斷邏輯:

a)先根據參數的類型進行精確匹配查找,如未找到,則進行第二步查找;

b)得到全部的構造方法遍歷,經過參數數量過濾,再比對形參類型與實參類型

1.4.2 當咱們判斷出構造方法或者工廠方法後,對於原型Bean,下次獲取是否就能夠省去判斷了?

也就是說,對於原型Bean,咱們能夠緩存下這個構造方法或工廠方法。如何實現?

在BeanDefinition中增長緩存的方法:

    /* 下面的四個方法是供beanFactory中使用的 */
    //獲取構造方法
    public Constructor<?> getConstructor();

    //緩存構造方法
    public void setConstructor(Constructor<?> constructor);

    //獲取工廠方法
    public Method getFactoryMethod();

    //緩存工廠方法
    public void setFactoryMethod(Method factoryMethod);

 在GenericBeanDefinition中增長對應的實現:

    /* 下面的四個方法是供beanFactory中使用的 */
    //獲取構造方法
    public Constructor<?> getConstructor() {
        return constructor;
    }

    //緩存構造方法
    public void setConstructor(Constructor<?> constructor) {
        this.constructor = constructor;
    }

    //獲取工廠方法
    public Method getFactoryMethod() {
        return factoryMethod;
    }

    //緩存工廠方法
    public void setFactoryMethod(Method factoryMethod) {
        this.factoryMethod = factoryMethod;
    }

1.4.3 接下來能夠寫查找構造方法或查找工廠方法的代碼了

1)在DefaultBeanFactory中增長查找構造方法的方法

    //查找構造方法的方法
    private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception {

        Constructor<?> ct = null;

        //若是參數爲空,則返回無參的構造方法
        if (args == null) {
            return bd.getBeanClass().getConstructor(null);
        }

        // 對於原型bean,從第二次開始獲取bean實例時,可直接得到第一次緩存的構造方法。
        ct = bd.getConstructor();
        if (ct != null) {
            return ct;
        }

        // 先根據參數類型獲取精確匹配的構造方法
        Class<?>[] paramTypes = new Class[args.length];
        int j = 0;
        for (Object p : args) {
            paramTypes[j++] = p.getClass();
        }
        try {
            ct = bd.getBeanClass().getConstructor(paramTypes);
        } catch (Exception e) {
            // 這個異常不須要處理
        }

        //若是根據參數類型進行精確批次查找沒有找到構造方法,就得到全部的構造方法遍歷,經過參數數量過濾,再比對形參類型與實參類型
        if (ct == null) {

            // 沒有精確參數類型匹配的,則遍歷匹配全部的構造方法
            // 判斷邏輯:先判斷參數數量,再依次比對形參類型與實參類型
            outer: for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) {
                Class<?>[] paramterTypes = ct0.getParameterTypes();
                if (paramterTypes.length == args.length) {
                    for (int i = 0; i < paramterTypes.length; i++) {
                        //isAssignableFrom方法表示是否能夠把args[i].getClass()賦值給paramterTypes[i]
                        if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {
                            continue outer;
                        }
                    }

                    ct = ct0;
                    break outer;
                }
            }
        }

        //若是找到構造方法了,而且是原型的就緩存起來
        if (ct != null) {
            // 對於原型bean,能夠緩存找到的構造方法,方便下次構造實例對象。在BeanDefinfition中獲取設置所用構造方法的方法。
            // 同時在上面增長從beanDefinition中獲取的邏輯。
            if (bd.isPrototype()) {
                bd.setConstructor(ct);
            }
            return ct;
        } else {
            throw new Exception("不存在對應的構造方法!" + bd);
        }
    }

 2)修改DefaultBeanFactory中用構造方法建立實例的代碼調用determineConstructor

    // 構造方法來構造對象
    private Object createInstanceByConstructor(BeanDefinition bd) throws Exception {
        try {
            //獲取bean定義中的構造參數
            Object[] args = this.getConstructorArgumentValues(bd);
            //若是構造參數爲空就使用無參構造函數
            if (args == null) {
                return bd.getBeanClass().newInstance();
            } 
            //查找有參構造函數並返回
            else {
                // 決定構造方法
                return this.determineConstructor(bd, args).newInstance(args);
            }
        } catch (SecurityException e1) {
            logger.error("建立bean的實例異常,beanDefinition:" + bd, e1);
            throw e1;
        }
    }

3)按照增長查找構造方法的方式修改靜態工廠方法、工廠方法方式的參數依賴

增長查找工廠方法的方法

    //查找工廠方法的方法
    private Method determineFactoryMethod(BeanDefinition bd, Object[] args, Class<?> type) throws Exception {
        if (type == null) {
            type = bd.getBeanClass();
        }

        //獲取bean定義中國工廠方法的名字
        String methodName = bd.getFactoryMethodName();

        //若是參數爲空就返回無參的方法
        if (args == null) {
            return type.getMethod(methodName, null);
        }

        Method m = null;
        // 對於原型bean,從第二次開始獲取bean實例時,可直接得到第一次緩存的構造方法。
        m = bd.getFactoryMethod();
        if (m != null) {
            return m;
        }

        // 先根據參數類型獲取精確匹配的方法
        Class[] paramTypes = new Class[args.length];
        int j = 0;
        for (Object p : args) {
            paramTypes[j++] = p.getClass();
        }
        try {
            m = type.getMethod(methodName, paramTypes);
        } catch (Exception e) {
            // 這個異常不須要處理
        }

        //若是根據參數類型進行精確批次查找沒有找到工廠方法,就得到全部的構造方法遍歷,經過參數數量過濾,再比對形參類型與實參類型
        if (m == null) {

            // 沒有精確參數類型匹配的,則遍歷匹配全部的方法
            // 判斷邏輯:先判斷參數數量,再依次比對形參類型與實參類型
            outer: for (Method m0 : type.getMethods()) {
                if (!m0.getName().equals(methodName)) {
                    continue;
                }
                Class<?>[] paramterTypes = m.getParameterTypes();
                if (paramterTypes.length == args.length) {
                    for (int i = 0; i < paramterTypes.length; i++) {
                        //isAssignableFrom方法表示是否能夠把args[i].getClass()賦值給paramterTypes[i]
                        if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) {
                            continue outer;
                        }
                    }

                    m = m0;
                    break outer;
                }
            }
        }

        //若是找到構造方法了,而且是原型的就緩存起來
        if (m != null) {
            // 對於原型bean,能夠緩存找到的方法,方便下次構造實例對象。在BeanDefinfition中獲取設置所用方法的方法。
            // 同時在上面增長從beanDefinition中獲取的邏輯。
            if (bd.isPrototype()) {
                bd.setFactoryMethod(m);
            }
            return m;
        } else {
            throw new Exception("不存在對應的構造方法!" + bd);
        }
    }

修改DefaultBeanFactory中用工廠方法建立實例的代碼調用determineFactoryMethod

    // 靜態工廠方法
    private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {

        Class<?> type = bd.getBeanClass();
        Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues());
        Method m = this.determineFactoryMethod(bd, realArgs, null);
        return m.invoke(type, realArgs);
    }

    // 工廠bean方式來構造對象
    private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {

        Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
        Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues());
        Method m = this.determineFactoryMethod(bd, realArgs, factoryBean.getClass());

        return m.invoke(factoryBean, realArgs);
    }

1.4.4 測試

參數依賴注入測試

package v2;

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

import org.junit.Test;

import com.dn.spring.beans.BeanReference;
import com.dn.spring.beans.GenericBeanDefinition;
import com.dn.spring.beans.PreBuildBeanFactory;
import com.dn.spring.samples.ABean;
import com.dn.spring.samples.ABeanFactory;
import com.dn.spring.samples.CBean;
import com.dn.spring.samples.CCBean;

/**
 * 
 * @Description: 參數依賴注入測試
 * @author leeSmall
 * @date 2018年12月1日
 *
 */
public class DItest {
    static PreBuildBeanFactory bf = new PreBuildBeanFactory();

    //構造函數參數依賴注入
    @Test
    public void testConstructorDI() throws Exception {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ABean.class);
        List<Object> args = new ArrayList<>();
        args.add("abean01");
        args.add(new BeanReference("cbean"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("abean", bd);

        bd = new GenericBeanDefinition();
        bd.setBeanClass(CBean.class);
        args = new ArrayList<>();
        args.add("cbean01");
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("cbean", bd);

        bf.preInstantiateSingletons();

        ABean abean = (ABean) bf.getBean("abean");

        abean.doSomthing();
    }

    //靜態工廠方法參數依賴注入
    @Test
    public void testStaticFactoryMethodDI() throws Exception {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ABeanFactory.class);
        bd.setFactoryMethodName("getABean");
        List<Object> args = new ArrayList<>();
        args.add("abean02");
        args.add(new BeanReference("cbean02"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("abean02", bd);

        bd = new GenericBeanDefinition();
        bd.setBeanClass(CBean.class);
        args = new ArrayList<>();
        args.add("cbean02");
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("cbean02", bd);

        bf.preInstantiateSingletons();

        ABean abean = (ABean) bf.getBean("abean02");

        abean.doSomthing();
    }

    //普通工廠方法的參數依賴注入
    @Test
    public void testFactoryMethodDI() throws Exception {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setFactoryBeanName("abeanFactory");
        bd.setFactoryMethodName("getABean2");
        List<Object> args = new ArrayList<>();
        args.add("abean03");
        args.add(new BeanReference("cbean02"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("abean03", bd);

        bd = new GenericBeanDefinition();
        bd.setBeanClass(ABeanFactory.class);
        bf.registerBeanDefinition("abeanFactory", bd);

        bf.preInstantiateSingletons();

        ABean abean = (ABean) bf.getBean("abean03");

        abean.doSomthing();
    }

}

輸出結果:

調用了含有CBean參數的構造方法
1543644406604 abean01 cb.name=cbean01

調用了含有CBean參數的構造方法
1543644406607 abean02 cb.name=cbean02

調用了含有CBean參數的構造方法
1543644406608 abean03 cb.name=cbean02

1.4.5 循環依賴如何處理

問題:構造對象時能夠循環依賴嗎/

構造實例對象時的循環依賴,會陷入僵死局面,是不容許構造實例時的循環依賴的。那麼怎麼發現循環依賴呢?

發現循環依賴的方法:加入一個正在構造的bean的記錄,每一個bean開始構造時加入該記錄中,構造完成後從記錄中移除。若是有依賴,先看依賴的bean是否在構造中,如是就構成了循環依賴,拋出異常

代碼實現:

在DefaultBeanFactory增長以下代碼:

        //記錄正在構造的bean
    private ThreadLocal<Set<String>> buildingBeans = new ThreadLocal<>();

        // 記錄正在建立的Bean
        Set<String> ingBeans = this.buildingBeans.get();
        if (ingBeans == null) {
            ingBeans = new HashSet<>();
            this.buildingBeans.set(ingBeans);
        }

        // 檢測循環依賴 由於若是已bean正在構造,這時發現有其餘的bean依賴它,此時它又沒有建立很久須要拋異常了
        if (ingBeans.contains(beanName)) {
            throw new Exception(beanName + " 循環依賴!" + ingBeans);
        }

        // 記錄正在建立的Bean
        ingBeans.add(beanName);
               .....................
               ......................
        // 建立好實例後,移除建立中記錄
        ingBeans.remove(beanName);

循環依賴測試代碼

package v2;

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

import org.junit.Test;

import com.dn.spring.beans.BeanReference;
import com.dn.spring.beans.GenericBeanDefinition;
import com.dn.spring.beans.PreBuildBeanFactory;
import com.dn.spring.samples.DBean;
import com.dn.spring.samples.EBean;

/**
 * 
 * @Description: 循環依賴測試
 * @author leeSmall
 * @date 2018年12月1日
 *
 */
public class CirculationDiTest {

    static PreBuildBeanFactory bf = new PreBuildBeanFactory();

    //DBean依賴EBean,EBean又依賴DBean 拋異常
    @Test
    public void testCirculationDI() throws Exception {
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(DBean.class);
        List<Object> args = new ArrayList<>();
        args.add(new BeanReference("ebean"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("dbean", bd);

        bd = new GenericBeanDefinition();
        bd.setBeanClass(EBean.class);
        args = new ArrayList<>();
        args.add(new BeanReference("dbean"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("ebean", bd);

        bf.preInstantiateSingletons();
    }
}

2. 屬性依賴

問題1:屬性依賴是什麼?

  某個屬性依賴某個值,須要外部給入這個值

問題2:該如何來描述一個屬性依賴?

  屬性名、值,定義一個來來表示這兩個值

問題3:會有多個屬性依賴,怎麼存放?

  List存放

問題4:屬性值得狀況和構造參數值得狀況同樣嗎?

  同樣

2.1 定義屬性依賴實體

屬性依賴實體類類圖以下:

屬性依賴實體類代碼:

package com.study.spring.beans;

/**
 * 
 * @Description: 屬性值依賴實體
 * @author leeSmall
 * @date 2018年12月1日
 *
 */
public class PropertyValue {

    private String name;

    private Object value;

    public PropertyValue(String name, Object value) {
        super();
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

}

 2.2 在BeanDefinition接口中增長得到屬性依賴定義的方法

    /**
     * 得到屬性依賴定義的方法
     * 
     */
    List<PropertyValue> getPropertyValues();

 2.3 在GenericBeanDefinition增長對應的實現

    //存放屬性依賴
    private List<PropertyValue> propertyValues;

    //獲取屬性依賴
    public List<PropertyValue> getPropertyValues() {
        return propertyValues;
    }

    //設置屬性依賴
    public void setPropertyValues(List<PropertyValue> propertyValues) {
        this.propertyValues = propertyValues;
    }

 2.4 在DefaultBeanFactory中實現屬性依賴

    //屬性依賴實現
    private void setPropertyDIValues(BeanDefinition bd, Object instance) throws Exception {
        //bean定義中沒有屬性依賴就直接返回
        if (CollectionUtils.isEmpty(bd.getPropertyValues())) {
            return;
        }
        //若是bean定義中有屬性依賴就設置值,設置方式和構造參數的設置同樣
        for (PropertyValue pv : bd.getPropertyValues()) {
            //屬性依賴沒有給名字就直接跳過
            if (StringUtils.isBlank(pv.getName())) {
                continue;
            }
            //獲取類對象
            Class<?> clazz = instance.getClass();
            //經過屬性名獲取類對象裏面聲明的字段
            Field p = clazz.getDeclaredField(pv.getName());

            //設置字段可訪問
            p.setAccessible(true);

            //把屬性值轉化爲真正的值,和構造參數同樣
            Object rv = pv.getValue();
            Object v = null;
            if (rv == null) {
                v = null;
            } else if (rv instanceof BeanReference) {
                v = this.doGetBean(((BeanReference) rv).getBeanName());
            } else if (rv instanceof Object[]) {
                // TODO 處理集合中的bean引用
            } else if (rv instanceof Collection) {
                // TODO 處理集合中的bean引用
            } else if (rv instanceof Properties) {
                // TODO 處理properties中的bean引用
            } else if (rv instanceof Map) {
                // TODO 處理Map中的bean引用
            } else {
                v = rv;
            }

            //把真正的值v設置到屬性p裏面 即屬性p依賴的值v
            p.set(instance, v);

        }
    }

 在doGetBean(String beanName)中增長對設置屬性依賴的調用

        // 給入屬性依賴
        this.setPropertyDIValues(bd, instance);

注意:屬性依賴是容許循環依賴的,由於是在實例建立好以後才設置屬性依賴的值的!!!

 2.5 屬性依賴測試

package v2;

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

import org.junit.Test;

import com.dn.spring.beans.BeanReference;
import com.dn.spring.beans.GenericBeanDefinition;
import com.dn.spring.beans.PreBuildBeanFactory;
import com.dn.spring.beans.PropertyValue;
import com.dn.spring.samples.ABean;
import com.dn.spring.samples.CBean;
import com.dn.spring.samples.FBean;

/**
 * 
 * @Description: 屬性依賴測試
 * @author leeSmall
 * @date 2018年12月1日
 *
 */
public class PropertyDItest {
    static PreBuildBeanFactory bf = new PreBuildBeanFactory();

    @Test
    public void testPropertyDI() throws Exception {

        //構造參數依賴
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ABean.class);
        List<Object> args = new ArrayList<>();
        args.add("abean01");
        args.add(new BeanReference("cbean"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("abean", bd);

        //構造參數依賴
        bd = new GenericBeanDefinition();
        bd.setBeanClass(CBean.class);
        args = new ArrayList<>();
        args.add("cbean01");
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("cbean", bd);

        //屬性依賴
        bd = new GenericBeanDefinition();
        bd.setBeanClass(FBean.class);
        List<PropertyValue> propertyValues = new ArrayList<>();
        propertyValues.add(new PropertyValue("name", "FFBean01"));
        propertyValues.add(new PropertyValue("age", 18));
        propertyValues.add(new PropertyValue("aBean", new BeanReference("abean")));
        bd.setPropertyValues(propertyValues);
        bf.registerBeanDefinition("fbean", bd);

        bf.preInstantiateSingletons();

        FBean fbean = (FBean) bf.getBean("fbean");
        System.out.println("設置的屬性依賴值: " + fbean.getName() + " " + fbean.getAge());
        fbean.getaBean().doSomthing();
    }

}

輸出結果:

調用了含有CBean參數的構造方法
設置的屬性依賴值: FFBean01 18
1543649718902 abean01 cb.name=cbean01

 完整代碼獲取地址:

 Spring IOC分析和手寫實現Spring IOC代碼:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v1

Spring DI分析和手寫實現Spring DI代碼:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v2

相關文章
相關標籤/搜索