教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

手動實(shí)現(xiàn)IOC容器:SpringIOC底層實(shí)現(xiàn)原理

更新時間:2020年08月14日15時25分 來源:傳智播客 瀏覽次數(shù):

面試官特別愛問SpringIOC底層實(shí)現(xiàn),Spring源碼晦澀難懂怎么辦呢? 跟著老師手動實(shí)現(xiàn)一個mini ioc容器吧,實(shí)現(xiàn)后再回頭看Spring源碼事半功倍哦,就算直接和面試官講也完全可以哦,類名完全按照源碼設(shè)計,話不多說開干~!

手動實(shí)現(xiàn)IOC容器的設(shè)計

需要實(shí)現(xiàn)的IOC功能:

·可以通過xml配置bean信息

·可以通過容器getBean獲取對象

·能夠根據(jù)Bean的依賴屬性實(shí)現(xiàn)依賴注入

·可以配置Bean的單例多例

實(shí)現(xiàn)簡易IOC設(shè)計的類

類/接口 說明
BeanFactory IOC容器的基礎(chǔ)接口,提供IOC容器的基本功能
DefaultListableBeanFactory IOC容器的核心實(shí)現(xiàn)類,提供多個map集合用來存儲bean的定義對象,提供getBean方法的核心實(shí)現(xiàn)
XmlBeanFactory IOC容器的實(shí)現(xiàn)類,基于xml構(gòu)建bean信息
XmlBeanDefinitionReader 用于解析xml信息,并提供解析Document文檔的方法,并將解析到的BeanDefinition對象注冊到核心容器中
BeanDefinition 封裝Bean的定義對象,如: bean的id class,scope ..等等
Property 封裝Bean所關(guān)聯(lián)依賴的屬性

類之間關(guān)系模型

1597377196425_IOC01.jpg

前期準(zhǔn)備

創(chuàng)建maven項目引入依賴


<dependencies>
    <!-- 解析xml -->
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.1</version>
    </dependency>
    <!-- BeanUtils    -->
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.3</version>
    </dependency>
</dependencies>

準(zhǔn)備3個bean的實(shí)體類


/**
 * 學(xué)生類  
 * 學(xué)生類依賴班級對象
 * 并提供 sayHello() 方法
 * @作者 itcast
 * @創(chuàng)建日期 2020/3/7 19:46
 **/
public class Student {
    private String name;
    private TClass tClass;
    public void sayHello(){
        System.out.println("大家好,我是" +this.name+" 我的班級是==>"+tClass.getCname() + " 我的老師是"+tClass.getTeacher().getTname());
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public TClass gettClass() {
        return tClass;
    }
    public void settClass(TClass tClass) {
        this.tClass = tClass;
    }
}
/**
 * 班級類
 * 班級類依賴教師對象
 * @作者 itcast
 * @創(chuàng)建日期 2020/3/7 19:45
 **/
public class TClass {
    private String cname;// 班級名稱
    private Teacher teacher// 老師
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public com.itcast.ioc.bean.Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(com.itcast.ioc.bean.Teacher teacher) {
        this.teacher = teacher;
    }
}
/**
 * 教師類
 * @作者 itcast
 * @創(chuàng)建日期 2020/3/7 19:44
 **/
public class Teacher {
    private String tname;// 老師名稱
    public String getTname() {
        return tname;
    }
    public void setTname(String tname) {
        this.tname = tname;
    }
}


xml配置對象

配置學(xué)生對象: 小明

依賴班級對象: 3年2班

依賴教師對象: 陳老師


<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- 配置IOC容器要管理的對象   bean作用域: 單例  原型 -->
    <bean id="studentclass="com.itcast.ioc.bean.Studentscope="singletonlazy-init="true">
        <!-- 依賴注入:   屬性注入    構(gòu)造器注入   注解注入-->
        <property name="namevalue="小明"></property>
        <property name="tClassref="tclass"></property>
    </bean>
    <bean id="tclassclass="com.itcast.ioc.bean.TClass">
        <property name="cnamevalue="3年2班"></property>
        <property name="teacherref="teacher"></property>
    </bean>
    <bean id="teacherclass="com.itcast.ioc.bean.Teacher">
        <property name="tnamevalue="陳老師"></property>
    </bean>
</beans>

mini-IOC容器-定義類

定義BeanFactory



/**
 * 容器的基礎(chǔ)接口
 * 提供容器最基本的功能
 */
public interface BeanFactory {
    // 核心方法 獲取對象
    Object getBean(String beanName);
}


定義DefaultListableBeanFactory



package com.itcast.ioc.core;

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

/**
 * 基礎(chǔ)容器的核心實(shí)現(xiàn)
 * 提供 beanDefinitionMap 存儲bean的定義
 * 提供 singletonObjects 存儲bean的對象實(shí)例
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:37
 **/
public class DefaultListableBeanFactory implements BeanFactory {
    // 提供 beanDefinitionMap 存儲bean的定義
    private Map<String,BeanDefinitionbeanDefinitionMap = new ConcurrentHashMap<>();
    // 提供 singletonObjects 存儲bean的對象實(shí)例 (scope為singleton的)
    private Map<String,ObjectsingletonObjects = new ConcurrentHashMap<>();
    /**
     * 實(shí)現(xiàn)getBean方法
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        return null;
    }
    /**
     * 將bean注冊到容器中
     * @param beanDefinition
     */
    public void registerBeanDefinition(BeanDefinition beanDefinition){
        beanDefinitionMap.put(beanDefinition.getBeanName(),beanDefinition);
    }
}

定義BeanDefnition



/**
 * 用于描述Bean的定義
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:41
 **/
public class BeanDefinition {
    private String beanName// bean標(biāo)簽的ID 作為bean的唯一標(biāo)識
    private String className// bean的所屬class
    private String scope = "singleton";  // bean的scope作用域
    private List<PropertypropertyList = new ArrayList<>();
    public String getBeanName() {
        return beanName;
    }
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
    public String getClassName() {
        return className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
    public String getScope() {
        return scope;
    }
    public void setScope(String scope) {
        this.scope = scope;
    }
    public List<PropertygetPropertyList() {
        return propertyList;
    }
    public void setPropertyList(List<PropertypropertyList) {
        this.propertyList = propertyList;
    }
}

定義Property



/**
 * 用于封裝一個property標(biāo)簽
 * 屬性數(shù)據(jù)
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:44
 **/
public class Property {
    private String name// 屬性名稱
    private String value// 屬性的值
    private String ref// 屬性的引用
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
    public String getRef() {
        return ref;
    }
    public void setRef(String ref) {
        this.ref = ref;
    }
}


定義XmlBeanFactory



/**
 * 繼承核心實(shí)現(xiàn)類
 * 基于xml配置bean的實(shí)現(xiàn)類
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:47
 **/
public class XmlBeanFactory extends DefaultListableBeanFactory {
    /**
     * 將解析配置文件 注冊bean的所有工作交給reader對象
     */
    final XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(this);
    /**
     * 構(gòu)造器需要傳入xml配置文件
     * @param configPath
     */
    public XmlBeanFactory(String configPath) {
        // 使用reader對象 解析配置  注冊Bean
        this.xmlBeanDefinitionReader.loadBeanDefinitions(configPath);
    }
}

定義XmlBeanDefinitionReader



/**
 * 解析配置
 * 注冊到容器中
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:51
 **/
public class XmlBeanDefinitionReader {
    // 核心beanfactory對象 用于將解析后的bean注冊到beanfactory中
    final DefaultListableBeanFactory beanFactory;
    public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
    /**
     * 根據(jù)傳遞的配置文件
     * 解析配置
     * 注冊bean
     * @param configPath
     */
    void loadBeanDefinitions(String configPath){

    }
}

mini-IOC容器--解析注冊

實(shí)現(xiàn)步驟

1. 通過dom4j解析xml得到Document文檔

2. 遍歷文檔所有Bean標(biāo)簽

3. 解析每一個Bean標(biāo)簽 封裝一個BeanDefinition對象

4. 解析每一個Bean標(biāo)簽下的所有Property標(biāo)簽 封裝一個Property對象

5. 將BeanDefinition和Property對象注冊到容器

實(shí)現(xiàn)xml解析及bean注冊



/**
 * 解析配置
 * 注冊到容器中
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:51
 **/
public class XmlBeanDefinitionReader {
    // 核心beanfactory對象 用于將解析后的bean注冊到beanfactory中
    final DefaultListableBeanFactory beanFactory;
    public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
    /**
     * 根據(jù)傳遞的配置文件
     * 解析配置
     * 注冊bean
     * @param configPath
     */
    void loadBeanDefinitions(String configPath){
        // 1. 通過dom4j解析xml得到Document文檔
        Document document = doLoadDocument(configPath);
        // 2. 遍歷文檔所有Bean標(biāo)簽
        Element rootElement = document.getRootElement();
        List<Elementlist = rootElement.selectNodes("//bean");
        for (Element element : list) {
            // 3. 解析每一個Bean標(biāo)簽 封裝一個BeanDefinition對象
            BeanDefinition beanDefinition = parseBeanDefinition(element);
            // 5. 將BeanDefinition和Property對象注冊到容器
            beanFactory.registerBeanDefinition(beanDefinition);
        }
    }
    /**
     * 3. 解析每一個Bean標(biāo)簽 封裝一個BeanDefinition對象
     * 4. 解析每一個Bean標(biāo)簽下的所有Property標(biāo)簽 封裝一個Property對象
     */
    BeanDefinition parseBeanDefinition(Element element){
        BeanDefinition beanDefinition = new BeanDefinition();
        String beanName = element.attributeValue("id");
        String className = element.attributeValue("class");
        String scope = element.attributeValue("scope");
        beanDefinition.setBeanName(beanName);
        beanDefinition.setClassName(className);
        if(scope!=null&&!"".equals(scope)){
            beanDefinition.setScope(scope);
        }
        List<ElementpropertyList = element.elements("property");
        for (Element propertyEle : propertyList) {
            Property property = new Property();
            property.setName(propertyEle.attributeValue("name"));
            property.setValue(propertyEle.attributeValue("value"));
            property.setRef(propertyEle.attributeValue("ref"));
            beanDefinition.getPropertyList().add(property);
        }
        return beanDefinition;
    }
    /**
     * 解析Document文檔
     * @param configPath
     * @return
     */
    Document doLoadDocument(String configPath){
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath);
        SAXReader saxReader = new SAXReader();
        try {
            return saxReader.read(inputStream);
        } catch (DocumentException e) {
            e.printStackTrace();
            System.out.println("解析xml出現(xiàn)異常==>"+e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }
}

準(zhǔn)備測試類



/**
 * 測試類
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 16:19
 **/
public class IocTest {
    public static void main(String[] args) {
        // 創(chuàng)建IOC容器
        BeanFactory beanFactory = new XmlBeanFactory("applicationContext.xml");
        // 通過容器獲取對象
        Student student = (Student)beanFactory.getBean("student");
        // 調(diào)用對象sayHello方法
        student.sayHello();
    }
}

斷點(diǎn)查看注冊情況

可以看到我們配置的xml內(nèi)容 已經(jīng)解析成了BeanDefinition對象,注冊到了核心容器的map中

1597377245545_IOC02.jpg


mini-IOC容器-getBean

實(shí)現(xiàn)步驟

1. 先從單例的map集合中獲取 是否有指定beanName的對象

    ·有直接返回

    ·沒有下一步

2. 從注冊集合中獲取bean的定義對象

    ·有下一步

    ·沒有拋異常NoSuchBeanDefinition

3. 判斷bean的scope作用域

   singleton單例

       · createBean對象

        ·存入單例集合

        ·返回對象

prototype多例

        ·createBean對象

        ·返回對象

4. createBean方法

        獲取BeanDefinition中的className

        通過反射API得到Class對象

        通過反射API得到bean實(shí)例

        獲取BeanDefinition中依賴的屬性列表

        實(shí)現(xiàn)屬性的依賴注入

實(shí)現(xiàn)getBean及createBean方法


/**
 * 基礎(chǔ)容器的核心實(shí)現(xiàn)
 * 提供 beanDefinitionMap 存儲bean的定義
 * 提供 singletonObjects 存儲bean的對象實(shí)例
 * @作者 itcast
 * @創(chuàng)建日期 2020/7/8 15:37
 **/
public class DefaultListableBeanFactory implements BeanFactory {
    // 提供 beanDefinitionMap 存儲bean的定義
    private Map<String,BeanDefinitionbeanDefinitionMap = new ConcurrentHashMap<>();
    // 提供 singletonObjects 存儲bean的對象實(shí)例 (scope為singleton的)
    private Map<String,ObjectsingletonObjects = new ConcurrentHashMap<>();
    /**
     * 實(shí)現(xiàn)getBean方法
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
//        1. 先從單例的map集合中獲取 是否有指定beanName的對象
        Object singletonObj = singletonObjects.get(beanName);
//                有直接返回
        if(singletonObj!=null){
            return singletonObj;
        }
//                沒有下一步
//        2. 從注冊集合中獲取bean的定義對象
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//                有下一步
//        沒有拋異常NoSuchBeanDefinition
        if(beanDefinition==null){
            throw new RuntimeException("NoSuchBeanDefinition : 你找的 "+beanName+" 對象 不存在");
        }
//        3. 判斷bean的scope作用域
        String scope = beanDefinition.getScope();
//                singleton單例
        if("singleton".equals(scope)){
//        createBean對象
            Object obj = createBean(beanDefinition);
//        存入單例集合
            singletonObjects.put(beanName,obj);
//        返回對象
            return obj;
        }else {
//        prototype多例
//        createBean對象
            return createBean(beanDefinition);
//        返回對象
        }
    }
    /**
     * //4. createBean方法
     * //獲取BeanDefinition中的className
     * //通過反射API得到Class對象
     * //通過反射API得到bean實(shí)例
     * //獲取BeanDefinition中依賴的屬性列表
     * //實(shí)現(xiàn)屬性的依賴注入
     * 創(chuàng)建對象
     * @param beanDefinition
     * @return
     */
    Object createBean(BeanDefinition beanDefinition){
        String className = beanDefinition.getClassName();
        Class<?aClass;
        try {
            aClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("類未找到"+e.getMessage());
        }
        // 創(chuàng)建對象:
        Object obj;
        try {
            obj = aClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException("創(chuàng)建對象失敗"+e.getMessage());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("非法訪問"+e.getMessage());
        }
        // 依賴注入
        List<PropertypropertyList = beanDefinition.getPropertyList();
        for (Property property : propertyList) {
            String name = property.getName();
            String value = property.getValue();
            String ref = property.getRef();
            // 屬性名不為空 進(jìn)行注入
            if(name!=null&&!"".equals(name)){
                // 如果配置的是value值 直接注入
                if(value!=null&&!"".equals(value)){
                    Map<String,Stringparams = new HashMap<>();
                    params.put(name,value);
                    try {
                        BeanUtils.populate(obj,params);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException("非法訪問"+e.getMessage());
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                        throw new RuntimeException("調(diào)用目標(biāo)對象失敗"+e.getMessage());
                    }
                }
                // 如果配置的是ref需要獲取其它對象注入
                if(ref!=null&&!"".equals(ref)){
                    try {
                        BeanUtils.setProperty(obj,name,getBean(ref));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException("非法訪問"+e.getMessage());
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                        throw new RuntimeException("調(diào)用目標(biāo)對象失敗"+e.getMessage());
                    }
                }
            }
        }
        return obj;
    }
    /**
     * 將bean注冊到容器中
     * @param beanDefinition
     */
    public void registerBeanDefinition(BeanDefinition beanDefinition){
        beanDefinitionMap.put(beanDefinition.getBeanName(),beanDefinition);
    }
}


mini-IOC容器-單例對象初始化
DefaultListableBeanFactory增加初始化方法

public void preInstaniceSingletons(){
        beanDefinitionMap.forEach((beanName,beanDefinition)->{
            String scope = beanDefinition.getScope();
            // 判斷單例  非抽象   不懶加載
            if("singleton".equals(scope)){
                this.getBean(beanName);
            }
        });
    }


XmlBeanFactory增加單例對象初始化

public XmlBeanFactory(String configPath) {
   // 使用reader對象 解析配置  注冊Bean
   this.xmlBeanDefinitionReader.loadBeanDefinitions(configPath);
   // 初始化單例對象
   this.preInstaniceSingletons();
}

mini-IOC容器-測試和小結(jié)

測試對象能否獲取

1597377257811_IOC03.jpg


查看bean的注冊及單例集合信息

可以通過變更scope的值查看對應(yīng)的變化

1597377267020_IOC04.jpg


IOC容器源碼及其它面試細(xì)節(jié)

擴(kuò)展: 容器如何創(chuàng)建對象

IOC容器在準(zhǔn)備創(chuàng)建對象時, 會判斷是否有配置 factory-method方法

如果有配置 會調(diào)用factory-method所指向的方法構(gòu)建對象.

如果沒配置,會檢查是否有配置構(gòu)造參數(shù)

無構(gòu)造參數(shù): 調(diào)用默認(rèn)構(gòu)造器創(chuàng)建對象

有構(gòu)造參數(shù): 根據(jù)參數(shù)情況匹配對應(yīng)的構(gòu)造器

擴(kuò)展: bean的生命周期

spring 容器中的bean的完整生命周期一共分為十一步完成。

1.bean對象的實(shí)例化

2.封裝屬性,也就是設(shè)置properties中的屬性值

3.如果bean實(shí)現(xiàn)了BeanNameAware,則執(zhí)行setBeanName方法,也就是bean中的id值

4.如果實(shí)現(xiàn)BeanFactoryAware或者ApplicationContextAware ,需要設(shè)置setBeanFactory或者上下文對象setApplicationContext

5.如果存在類實(shí)現(xiàn)BeanPostProcessor后處理bean,執(zhí)行postProcessBeforeInitialization,可以在初始化之前執(zhí)行一些方法

6.如果bean實(shí)現(xiàn)了InitializingBean,則執(zhí)行afterPropertiesSet,執(zhí)行屬性設(shè)置之后的操作

7.調(diào)用執(zhí)行指定的初始化方法

8.如果存在類實(shí)現(xiàn)BeanPostProcessor則執(zhí)行postProcessAfterInitialization,執(zhí)行初始化之后的操作

9.執(zhí)行自身的業(yè)務(wù)方法

10.如果bean實(shí)現(xiàn)了DisposableBean,則執(zhí)行spring的的銷毀方法

11.調(diào)用執(zhí)行自定義的銷毀方法。

擴(kuò)展: bean的循環(huán)依賴問題

A 依賴 B B 依賴 A 產(chǎn)生閉環(huán),稱為循環(huán)依賴

·Spring 默認(rèn)允許單例對象的屬性注入 所產(chǎn)生的循環(huán)依賴

單例對象的循環(huán)依賴 Spring通過3級緩存來解決

比如一個類A中有一個屬性是B類,B類中有一個屬性是A類,這時看Spring是怎么解決他們的相互依賴的。Spring注入一個類的大體步驟分為兩部分,一是先完成對類的構(gòu)造工作,二是會對類的屬性進(jìn)行設(shè)置和填充。首先Spring構(gòu)造A類,通過AbstractAutowireCapableBeanFactory的doCreateBean方法中調(diào)用addSingletonFactory方法將A類曝光到singletonFactories中。這時完成A的構(gòu)造后,需要填充B屬性,繼續(xù)第二步,發(fā)現(xiàn)B還沒有構(gòu)造,于是開始B流程的構(gòu)造過程,構(gòu)造的時候發(fā)現(xiàn)需要填充A,從第三層緩存singletonFactories中找到A(此時的A還沒有完全構(gòu)造完成,但是可以拿到A的一個引用),B拿到A的引用后,完成B自己的填充屬性工作,完成初始化工作,把自己放到第一層緩存singletonObjects中。這時回到A的這邊,在拿到B對象后,完成自己的填充屬性工作。

源碼 級別 描述
singletonObjects 一級緩存 用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用
earlySingletonObjects 二級緩存 存放原始的 bean 對象(尚未填充屬性),用于解決循環(huán)依賴
singletonFactories 三級緩存 存放 bean 工廠對象,用于解決循環(huán)依賴


·如果是構(gòu)造器依賴屬性 會報循環(huán)依賴異常

·如果對象都是多例對象 會報循環(huán)依賴異常

·如果設(shè)置allowCircularReferences為false 會報循環(huán)依賴異常



protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    if (this.allowBeanDefinitionOverriding != null) {
       beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.allowCircularReferences != null) {
       beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
 }


擴(kuò)展: bean的覆蓋問題

默認(rèn)情況:

同一個配置文件中出現(xiàn)id相同的bean會報錯,不同的配置文件出現(xiàn)id相同的bean后加,載的bean會將先加載的bean覆蓋掉稱為bean的覆蓋,bean的覆蓋不會報錯,但可能影響我們的項目,可以通過屬性設(shè)置不允許bean的覆蓋,allowBeanDefinitionOverriding設(shè)置為false。


猜你喜歡:

什么是Shiro?Shiro有什么特點(diǎn)?

Shiro身份認(rèn)證流程

什么是系統(tǒng)授權(quán)?系統(tǒng)授權(quán)流程介紹

java高級軟件工程師課程

0 分享到:
和我們在線交談!