1. 程式人生 > 實用技巧 >手寫Spring Ioc框架

手寫Spring Ioc框架

一、Spring體系結構

  1、Spring是一個輕量級一站式企業開發解決方案。

Spring成員 描述
Spring framework spring的基礎,包括ioc、aop及spring mvc、spring template等
Spring boot 儘量減少框架本身的學習成本以及開發成本,讓開發人員更多的關注和開發業務程式碼;主要特點就是 純註解、零配置(無Spring配置檔案)、自動裝配、起步依賴
Spring Cloud 分散式/微服務一站式開發框架
Spring data 解決的是資料互動(mysql、redis、elasticsearch、mongodb、orm等)
Spring security 主要是針對登陸安全、許可權認證等
Spring oauth2 解決單點登入、使用者鑑權等問題

  2、Spring framework的體系結構入下圖所示。

  對每一個模組的描述如下表格:

模組 子模組 描述
核心容器 Spring-Beans、Spring-Core

1、這兩個模組是Spring的核心模組,包含了控制反轉了依賴注入;

2、其中BeanFactory介面是Spring框架中的核心介面,他是工廠模式的具體實現

3、BeanFactory使用控制反轉和依賴注入解耦了配置、依賴性規範與業務程式碼

4、BeanFactory容器例項化後並不會自動例項化Bean,而是等到Bean被使用時,BeanFactory才會對Bean進行例項化和依賴關係裝配

Spring-Context

1、SpringContext模組架構於核心模組之上,他擴充套件了BeanFactory,為他添加了Bean生命週期控制、框架事件體系以及資源載入透明化等功能

2、ApplicationContext是該模組的核心介面,他是BeanFactory的超類

3、ApplicationContext容器例項化後會對所有的單例Bean進行例項化和依賴關係的裝配,使其處於可用狀態

Expression Expression是統一表達式語言的擴充套件模組,可以查詢和管理執行中的物件
AOP和裝置支援 Spring-AOP Spring-AOP是Spring的另一個核心模組,是AOP主要的實現模組
Spring-Aspects Spring-Aspects模組整合自AspectJ,主要是為Spring-AOP提供多種AOP實現方案
Spring-Instrument Spring-Instrument模組應該是AOP的支援模組,主要作用是在JVM啟動時,生成一個代理類,開發人員可以通過代理類在執行時改變類的位元組,從而改變一個類的功能,從而實現AOP功能
資料訪問與整合 Spring-JDBC

1、Spring-JDBC模組是Spring提供的JDBC抽象框架的主要實現模組,用於簡化SpringJDBC

2、Spring-JDBC主要提供模板方式、關係資料庫物件化方式、SimpleJdbc方式、事務管理來簡化JDBC程式設計

3、Spring-JDBC的主要實現類是JdbcTemplate、SimpleJdbcTemplate、NamedParameterJdbcTemplater

資料訪問及整合 Spring-TX Spring-TX是SpringJDBC的事務控制實現模組。
Spring-ORM Spring-ORM模組是ORM框架支援模組,主要整合Hibernate、JPA等用於資源管理、資料訪問的實現和事務策略
Spring-JMS Spring-JMS能夠傳送和介面資訊,從Spring4開始,還提供了Spring-Messaging模組的支撐
Spring-OXM5

Spring-OXM模組主要提供一個抽象層以支撐OXM:將JAVA物件對映成XML資料,或者將XML資料對映成JAVA物件

Web Spring-Web Spring-Web為Spring提供了最基礎的Web支援,主要建立在核心容器之上,通過Servlet或者Listeners來初始化IOC容器,也包含一些與Web相關的支援
Spring-WebMVC Spring-WebMVC是一個Web-Servlet模組,實現了SpringMVC的Web應用
Spring-WebSocket Spring-WebSocket主要是與Web前端的全雙工通訊的協議
Spring-WebFlux Spring-WebFlux是一個新的非阻塞函式式ReactiveWeb框架,可以用來建立非同步的,非阻塞,事件驅動的服務,並且擴充套件性非常好
報文傳送 Spring-Messaging Spring-Messaging是從Spring4開始加入的新模組,主要職責是為Spring框架整合一些基礎的報文傳送應用
Test Spring-Test Spring-Test主要為測試提供支援。

二、手寫Spring IOC框架

  手寫框架可以由簡入繁,一步步深入。

  以 apis--->service--->dao為例,進行資料查詢,dao中需要注入datasource,service需要注入dao,apis需要注入service。

  V1為直接手動賦值寫法,V2為使用了配置檔案載入的寫法,V3是借鑑了Spring的類體系後的寫法

1、V1版本(手動注入)

  IOC主要是使用了建構函式或者是setter方法進行了依賴注入,那麼V1版本就先模擬依賴注入部分。

  dao:

@Slf4j
public class UserDaoImpl implements UserDao {
    private BasicDataSource dataSource;
    public UserDaoImpl(BasicDataSource dataSource){
        this.dataSource = dataSource;
    }
    @Override
    public UserDo findUserById(String id) throws Exception {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        UserDo userDo = new UserDo();
        try {
            //載入資料庫驅動
            Class.forName(dataSource.getDriverClassName());
            //獲取資料庫連線
            connection = (Connection) DriverManager.getConnection(dataSource.getUrl(),dataSource.getUsername(),dataSource.getPassword());
            //sql預處理
            String sql = "select * from user where id = ?";
            preparedStatement = (PreparedStatement) connection.prepareStatement(sql);
            //引數設定
            preparedStatement.setString(1,id);
            //執行sql
            resultSet = preparedStatement.executeQuery();
            //迴圈結果集
            while (resultSet.next()){
                userDo.setId(resultSet.getInt("id"));
                userDo.setUsername(resultSet.getString("username"));
                userDo.setAddress(resultSet.getString("address"));
                userDo.setSex(resultSet.getString("sex"));
                userDo.setBirthday(resultSet.getDate("birthday"));
                log.info("查詢到使用者資訊,id=【{}】,username=【{}】", resultSet.getString("id"), resultSet.getString("username"));
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //釋放資源
            if (resultSet != null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return userDo;
    }
}

  service:

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    @Override
    public UserDo findUserById(String id) throws Exception{
        return userDao.findUserById(id);
    }
    public UserServiceImpl(UserDao userDao){
        this.userDao = userDao;
    }
}

  apis:

@Slf4j
public class UserApis {
    private UserService userService;
    public UserApis(UserService userService){
        this.userService = userService;
    }
    public void findUserById(String id) throws Exception{
        UserDo userDo = userService.findUserById(id);
        log.info("=================={}=================", userDo);
    }
}

  V1版本測試類:

    @Test
    public void writeFrameV1() throws Exception {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://*********:3306/ins_buying_0");
        dataSource.setUsername("*******");
        dataSource.setPassword("********");
        UserApis userApis = new UserApis(new UserServiceImpl(new UserDaoImpl(dataSource)));
        userApis.findUserById("2");
    }

  可以看到,直接將datasource注入到了UserDao,然後將UserDao注入到了UserService,將Userservice注入到了Userapis,最終完成呼叫,並在UserDao中使用datasource做了資料庫查詢操作。

2、V2版本(載入配置檔案)

  在寫之前,先梳理一下思路及應該有哪些物件:

    (1)應該有個spring的配置檔案,配置檔案中有bean標籤,標籤內有id、name、class、initMethod等屬性,同時可能存在屬性對應的property標籤(有可能多個),那麼這裡就使用一個BeanDefinition物件來封裝每一個Bean的配置資訊

    (2)property標籤中存在name、ref、value屬性,其中value屬性表示的是簡單型別,ref表示的是引用型別,這裡我們建立一個PropertyValue物件,來封裝property標籤,同時使用TypeStringValue來封裝簡單型別,使用RuntimeBeanRefeerance來封裝引用型別

    (3)對於載入流程,就是載入spring配置檔案進行解析,將所有的配置資訊放入一個map集合中

    (4)對於執行流程,我們使用一個單例map儲存單例的bean,保證單例bean只有一個,因此使用bean時,從單例map中獲取,如果獲取不到,就建立bean,如果建立成功,且是單例bean,則將bean放入單例map中

    (5)最重要的就是建立bean的步驟了,首先根據beanName從BeanDefinition中獲取配置資訊,然後使用反射獲取類物件,然後迴圈property集合,對集合中的每一個屬性進行賦值操作。

  總體流程如上所示,那麼接下來就一步步實現:

    (1)建立配置檔案及BeanDefinition物件

<beans>

    <bean id="userService" class="com.lcl.galaxy.spring.frame.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean id="userDao" class="com.lcl.galaxy.spring.frame.dao.UserDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://******:3306/ins_buying_0"/>
        <property name="username" value="*******"/>
        <property name="password" value="******"/>
    </bean>
</beans>
package com.lcl.galaxy.spring.frame.domain;

import lombok.Data;

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

@Data
public class MyBeanDefinition {

    private String clazzName;

    private String scope;

    private String beanName;

    private String initMethod;

    private List<MyPropertyValue> propertyValueList = new ArrayList<>();

    private final String SCOPE_SINGLETON = "singleton";
    private final String SCOPE_PROTOTYPE = "prototype";

    public MyBeanDefinition(String clazzName, String beanName) {
        this.clazzName = clazzName;
        this.beanName = beanName;
    }

    public boolean isSingleton(){
        if(SCOPE_SINGLETON.equals(scope)){
            return true;
        }
        return false;
    }

    public boolean isPrototype(){
        if(SCOPE_PROTOTYPE.equals(scope)){
            return true;
        }
        return false;
    }

    public void addPropertyValue(MyPropertyValue pv) {
        propertyValueList.add(pv);
    }
}

    (2)property標籤中存在name、ref、value屬性,其中value屬性表示的是簡單型別,ref表示的是引用型別,這裡我們建立一個PropertyValue物件,來封裝property標籤,同時使用TypeStringValue來封裝簡單型別,使用RuntimeBeanRefeerance來封裝引用型別

package com.lcl.galaxy.spring.frame.domain;

import lombok.Data;

@Data
public class MyPropertyValue {

    private String name;

    private Object typedStringValue;

    public MyPropertyValue(String name, Object typedStringValue) {
        this.name = name;
        this.typedStringValue = typedStringValue;
    }
}
package com.lcl.galaxy.spring.frame.domain;

import lombok.Data;

@Data
public class MyTypedStringValue {

    private String value;

    private Class<?>  targetType;

    public MyTypedStringValue(String value) {
        this.value = value;
    }
}
package com.lcl.galaxy.spring.frame.domain;

import lombok.Data;

@Data
public class MyRuntimeBeanReference {

    private String ref;

    public MyRuntimeBeanReference(String ref) {
        this.ref = ref;
    }
}

    (3)對於載入流程,就是載入spring配置檔案進行解析,將所有的配置資訊放入一個map集合中

    /**
     * 載入配置檔案
     */
    public void init() {
        String location = "write-frame-beans.xml";
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
        Document document = createDocument(inputStream);
        registerBeanDefinition(document.getRootElement());
    }

    /**
     * 輸入流傳喚為Document
     * @param inputStream
     * @return
     */
    private Document createDocument(InputStream inputStream) {
        Document document = null;
        SAXReader saxReader = new SAXReader();
        try {
            document = saxReader.read(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return document;
    }

    /**
     * 記載配置檔案
     * @param rootElement
     */
    private void registerBeanDefinition(Element rootElement) {
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            String name = element.getName();
            if ("bean".equals(name)) {
                parseDefaultElement(element);
            } else {
                parseCustomElement(element);
            }
        }
    }

    /**
     * 解析自定義標籤
     * @param element
     */
    private void parseCustomElement(Element element) {
    }

    /**
     * 解析bean標籤,封裝為Bedefinition,並將BeanDefinition放入map中
     * @param beanElement
     */
    private void parseDefaultElement(Element beanElement) {
        if (beanElement == null) {
            return;
        }

        String id = beanElement.attributeValue("id");
        String name = beanElement.attributeValue("name");
        String clazzName = beanElement.attributeValue("class");
        if (clazzName == null || "".equals(clazzName)) {
            return;
        }

        String initMethod = beanElement.attributeValue("initMethod");
        String scope = beanElement.attributeValue("scope");
        scope = scope != null ? scope : "singleton";
        String beanName = id == null ? name : id;
        Class<?> clazzType = null;
        try {
            clazzType = Class.forName(clazzName);
            beanName = beanName == null ? clazzType.getName() : beanName;
            MyBeanDefinition beanDefinition = new MyBeanDefinition(clazzName, beanName);
            beanDefinition.setInitMethod(initMethod);
            beanDefinition.setScope(scope);
            List<Element> propertyElements = beanElement.elements();
            for (Element propertyElement : propertyElements) {
                parsePropertyElement(beanDefinition, propertyElement);
            }
            beanDefinitions.put(beanName, beanDefinition);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    對property集合賦值

    /**
     * 為BeanDefinition的property集合賦值
     * @param beanDefinition
     * @param propertyElement
     */
    private void parsePropertyElement(MyBeanDefinition beanDefinition, Element propertyElement) {
        String name = propertyElement.attributeValue("name");
        String value = propertyElement.attributeValue("value");
        String ref = propertyElement.attributeValue("ref");

        if(value != null && !"".equals(value) && ref != null && !"".equals(ref)){
            return;
        }

        MyPropertyValue pv = null;
        if(value != null && !"".equals(value)){
            MyTypedStringValue typedStringValue = new MyTypedStringValue(value);
            Class<?> targetType = getTypeFieldName(beanDefinition.getClazzName(), name);
            typedStringValue.setTargetType(targetType);
            pv = new MyPropertyValue(name, typedStringValue);
            beanDefinition.addPropertyValue(pv);
        }else{
            MyRuntimeBeanReference runtimeBeanReference = new MyRuntimeBeanReference(ref);
            pv = new MyPropertyValue(name, runtimeBeanReference);
            beanDefinition.addPropertyValue(pv);
        }
    }

    /**
     * 獲取簡單型別
     * @param clazzName
     * @param name
     * @return
     */
    private Class<?> getTypeFieldName(String clazzName, String name) {
        try {
            Class<?> clazz = Class.forName(clazzName);
            Field field = clazz.getDeclaredField(name);
            return field.getType();
        } catch (Exception e) {
            log.info("================== clazzName,name ====================,{}==========,{}",clazzName, name);
            e.printStackTrace();
        }
        return null;
    }

    (4)對於執行流程,我們使用一個單例map儲存單例的bean,保證單例bean只有一個,因此使用bean時,從單例map中獲取,如果獲取不到,就建立bean,如果建立成功,且是單例bean,則將bean放入單例map中

    public void findUserById(String id) throws Exception {
        UserService userService = (UserService) getBean("userService");
        UserDo userDo = userService.findUserById(id);
        log.info("=================={}=================", userDo);
    }

    /**
     * 根據bean名稱獲取bean物件
     * @param beanName
     * @return
     */
    private Object getBean(String beanName) {
        Object object = singletonObjects.get(beanName);
        if (object != null) {
            return object;
        }

        MyBeanDefinition beanDefinition = beanDefinitions.get(beanName);
        if (beanDefinition == null || beanDefinition.getClazzName() == null) {
            return null;
        }

        if (beanDefinition.isSingleton()) {
            object = createBean(beanDefinition);
            this.singletonObjects.put(beanName, object);
        } else if (beanDefinition.isPrototype()) {
            object = createBean(beanDefinition);
        }
        return object;
    }

    (5)最重要的就是建立bean的步驟了,首先根據beanName從BeanDefinition中獲取配置資訊,然後使用反射獲取類物件,然後迴圈property集合,對集合中的每一個屬性進行賦值操作。

    /**
     * 建立bean
     * @param beanDefinition
     * @return
     */
    private Object createBean(MyBeanDefinition beanDefinition) {
        
        String clazzName = beanDefinition.getClazzName();
        try {
            Class<?> clazz = Class.forName(clazzName);
            if(clazz == null){
                return null;
            }
            Constructor<?> constructor = clazz.getConstructor();
            Object object = constructor.newInstance();
            populateBean(object, beanDefinition);
            initMethod();
           return object;
            
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 處理初始化方法
     */
    private void initMethod() {
    }

    /**
     * 為物件賦值
     * @param object
     * @param beanDefinition
     */
    private void populateBean(Object object, MyBeanDefinition beanDefinition) {
        List<MyPropertyValue> propertyValueList = beanDefinition.getPropertyValueList();
        for (MyPropertyValue propertyValue: propertyValueList) {
            String name = propertyValue.getName();
            Object value = propertyValue.getTypedStringValue();
            Object valueToUse = null;
            if(value instanceof MyTypedStringValue){
                MyTypedStringValue myTypedStringValue = (MyTypedStringValue) value;
                String stringValue = myTypedStringValue.getValue();
                Class<?> targetType = myTypedStringValue.getTargetType();
                if(targetType == Integer.class){
                    valueToUse = Integer.parseInt(stringValue);
                }else if(targetType == String.class){
                    valueToUse = String.valueOf(stringValue);
                }else{

                }
            }else if(value instanceof MyRuntimeBeanReference){
                MyRuntimeBeanReference myRuntimeBeanReference = (MyRuntimeBeanReference) value;
                valueToUse = getBean(myRuntimeBeanReference.getRef());
            }
            Class<?> clazz = object.getClass();
            try {
                Field declaredField = clazz.getDeclaredField(name);
                declaredField.setAccessible(true);
                declaredField.set(object, valueToUse);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

  可以看到,對於簡單型別的屬性,直接進行了賦值操作,對於引用型別,則使用了遞迴呼叫建立Bean物件,然後進行賦值操作

3、V3版本(仿照Spring原始碼類體系結構)

  由於V3版本是借鑑SpringIOC原始碼編寫,那先來看一下Spring IOC中的一些核心概念及類的繼承體系。

  基礎概念及對比

  Spring容器:Spring容器就是IOC容器,底層是一個BeanFactory(使用簡單工廠實現),spring容器分為基礎容器(BeanFactory)和高階容器(ApplicationContext)

  BeanFactory和ApplicationContext的區別:(1)ApplicationContext是繼承自BeanFactory介面;(2)BeanFactory建立Bean例項是在第一次獲取該Bean的例項時建立;(3)ApplicationContext建立Bean例項是在初始化的時候,一次性建立所有的單例bean例項。

  BeanPostProcessor和BeanFactoryPostProcessor的區別:(1)BeanFactoryPostProcessor是在建立Bean物件前,對Bean的封裝資訊Beandefinition物件做處理;例如context:property標籤,可以載入指定路徑的properties檔案,同時將檔案中的key-value放入容器;也可將BeanDefinition中的${}佔位符進行替換(2)BeanPostProcessor是在物件例項化後,對Bean物件做後置處理。例如:AOP

  BeanFactory和FactoryBean的區別:(1)BeanFactory是Spring的基礎容器,管理著spring中需要管理的所有Bean。(2)FactoryBean只是spring容器管理的bean的一員,只不過該bean物件比較特殊,可以產生一些指定型別的bean。  

  繼承體系 

  如上圖所示,是BeanFactory的類體系結構(這裡沒有包含AppllicationContext體系)可以將其描述為四級介面繼承體系。

  針對以上介面的大致內容描述如下:

介面級別 描述 BeanFactory介面 作用
一級介面 Beanfactory作為主介面,不繼承任何介面,可以稱為一級介面 BeanFactory 是Spring Bean的根介面,提供獲取bean、是否包含bean、是否單例、是否原型、獲取bean型別獲取bean等功能介面
二級介面 AutowireCapableBeanFactory、HierarchicalBeanFactory、ListableBeanFactory三個子介面繼承了BeanFactory,可以稱為二級介面 AutowireCapableBeanFactory 提供工廠的裝配功能
ListableBeanFactory 提供容器內bean例項的列舉功能,這邊不會考慮父容器內的例項
HierarchicalBeanFactory 提供父容器的訪問功能
三級介面 ConfigurableBeanFactory繼承了二級介面HierarchicalBeanFactory,同時和繼承了SingletonBeanRegistry介面 ConfigurableBeanFactory 提供factory的配置功能
四級介面 ConfigurableListableBeanFactory是一個更強大的介面,繼承了以上所有的介面。 ConfigurableListAbleBeanFactory 集大成者,提供解析bean、修改bean定義、初始化單例等功能

  根據上述內容,可以暫定一下手寫Ioc框架的類劃分

    1、載入主入口&呼叫主入口

    2、BeanFactory體系:用於載入Bendefinition資訊和建立Bean物件

    3、reader:用於讀取xml檔案或Document檔案

    4、registory:用於註冊BeanDefinition資訊和單例Bean資訊

    5、utils:用於處理Dodument物件或通過反射操作一些內容

    6、resource:載入配置檔案

    7、aware

  其中utils和resource屬於工具類範疇,在最後會附上程式碼。

  其實邏輯程式碼跟V2是一樣的,主要是對介面、類、方法進行了封裝,最主要的就是BeanFactory體系,仿照Spring原始碼中的BeanFactory體系,我也建立了一些體系,在說BeanFactory之前,先說一下registory的介面和類:

    registory

  我在程式碼中提供了兩個registory介面MyBeanDefinitonRegistory和MySingletonRegistory,分別用來註冊BeanDefinition和單例Bean物件

  其中MyBeanDefinitionRegistory介面提供了BeanDefinition註冊、根據beanName獲取BeanDefinition物件兩個方法

public interface MyBeanDefinitionRegisty {

    void registry(String beanName, MyBeanDefinition beanDefinition);

    MyBeanDefinition getBeanDefinition(String beanName);
}

  MySingletonRegistory提供了根據beanName獲取Bean例項物件和新增Bean例項物件兩個方法

package com.lcl.galaxy.spring.frame.V3.register;

public interface MySingletonRegistry {
    Object getSingletonBean(String beanName);

    void addSingleton(String beanName, Object object);
}

  為MySingletonRegistor提供了一個預設實現類MyDefaultSingletonBeanFactory,用來向單例Bean的map集合中新增單例Bean或獲取單例Bean

public class MyDefaultSingletonBeanRegistory implements MySingletonRegistry {

    private Map<String, Object> singletonObjects = new HashMap<>();

    @Override
    public Object getSingletonBean(String beanName) {
        return singletonObjects.get(beanName);
    }

    @Override
    public void addSingleton(String beanName, Object object) {
        singletonObjects.put(beanName, object);
    }
}

  BeanFactory

  說完registry,然後重點說一下BeanFactory類,仿照Spring原始碼中的BeanFactory體系建立的BeanFactory體系如下:

  首先,建立了一個根介面MyBeanFactory,該介面只提供根據BeanName獲取Bean物件

public interface MyBeanFactory {
    Object getBean(String beanName);
}

  建立了兩個二級介面MyAutowireCapableBeanFactory和MyListableBeanFactory,其中MyAutowireCapableBeanFactory提供了建立bean物件的方法,而MyListableBeanFactory則提供了根據指定型別獲取所有bean名稱集合方法和根據型別所有Bean物件集合方法

public interface MyAutowireCapableBeanFactory extends MyBeanFactory {
    Object createBean(String beanName, MyBeanDefinition myBeanDefinition);
}
public interface MyListableBeanFactory extends MyBeanFactory {
    List<String> getBeanNamesByType(Class<?> type);
    <T> List<T> getBeansByType(Class<?> type);
}

  然後建立介面的實現類,首先建立一個頂層的抽象類MyAbstructBeanFactory,其實現MyListableBeanFactory介面,同時整合上述的MyDefualtSingletonRegistory類,在該類中去實現getBean方法。

  在該類的getBean方法中,主要做了四步:單例bean是否已存在、獲取bean對應的BeanDefinition物件、根據BeanDefinition建立Bean例項、將Bean例項放入單例Bean的map中

  由於該類繼承了MyDefualtSingletonRegistory,因此也擁有了單例Bean的註冊和獲取方法,因此對於上面說的四步中,第一步和第四步直接可以呼叫父類的方法完成操作。

  那麼在該方法中,還有第二步獲取Bendefiniton物件和建立Bean例項兩個步驟,具體如何建立,交由子類處理,此處只使用抽象的方法做了封裝。(從這裡可以看到,具體每個類做哪些事情,還是劃分的比較清楚的)

public abstract class MyAbstractBeanFactory extends MyDefaultSingletonBeanRegistory implements MyListableBeanFactory {

    @Override
    public Object getBean(String beanName) {

        Object bean = getSingletonBean(beanName);
        if(bean != null){
            return bean;
        }
        MyBeanDefinition beanDefinition = getBeanDefinition(beanName);
        if(beanDefinition == null){
            return null;
        }

        if(beanDefinition.isSingleton()){
            bean = createBean(beanName, beanDefinition);
            addSingleton(beanName, beanDefinition);
        }else if(beanDefinition.isPrototype()){
            bean = createBean(beanName, beanDefinition);
        }
        return bean;
    }

    public abstract Object createBean(String beanName, MyBeanDefinition beanDefinition);

    public abstract MyBeanDefinition getBeanDefinition(String beanName);

}

  然後建立負責建立物件的BeanFactory:MyAbstructAutowireCapableBeanFactory,該類實現MyAutowireCapableBeanFactory介面,同時整合上一步的MyAbstructBeanFactory類,由於MyAbstructBeanFactory中有抽象的建立bean例項的方法,而MyAutowireCapableBeanFactory介面中也存在該方法,因此在MyAbstructAutowireCapableBeanFactory中實現該方法,具體實現內容其實和V2版本一致,具體就不再描述了,直接上程式碼:

public abstract class MyAbstructAutowireCapableBeanFactory extends MyAbstractBeanFactory implements MyAutowireCapableBeanFactory {
    @Override
    public Object createBean(String beanName, MyBeanDefinition beanDefinition) {
        Class<?> clazz = getResolvedClass(beanDefinition);
        Object object = createInstance(clazz);
        populateBean(object, beanDefinition);
        initalBean(object, beanDefinition);
        return object;
    }

    /**
     * bean的初始化
     * @param object
     * @param beanDefinition
     */
    private void initalBean(Object object, MyBeanDefinition beanDefinition) {
        //aware介面處理
        if(object instanceof MyAware){
            if(object instanceof MyBeanFactoryAware){
                ((MyBeanFactoryAware)object).setBeanFactory(this);
            }
        }
        //對實現了IniallizingBean介面的類,呼叫他的afterProperties方法
        //如果Bean中init-method屬性有值,則呼叫指定的方法
        initMethod(object, beanDefinition);
    }

    private void initMethod(Object object, MyBeanDefinition beanDefinition) {
        String method = beanDefinition.getInitMethod();
        if(method != null && !"".equals(method)){
            ReflectUtils.invokeMethod(object, method);
        }
    }

    private Object createInstance(Class<?> clazz){
        //如果有例項工廠,則通過例項工廠建立Bean例項
        //如果有工廠方法,則通過工廠方法建立Bean例項
        //如果都沒有,則使用建構函式建立Bean例項
        return ReflectUtils.createObject(clazz);
    }

    private Class<?> getResolvedClass(MyBeanDefinition beanDefinition){
        String clazzName = beanDefinition.getClazzName();
        try {
            Class<?> clazz = Class.forName(clazzName);
            return clazz;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 為物件賦值
     * @param object
     * @param beanDefinition
     */
    private void populateBean(Object object, MyBeanDefinition beanDefinition) {
        List<MyPropertyValue> propertyValueList = beanDefinition.getPropertyValueList();
        for (MyPropertyValue propertyValue: propertyValueList) {
            String name = propertyValue.getName();
            Object value = propertyValue.getTypedStringValue();
            Object valueToUse = null;
            if(value instanceof MyTypedStringValue){
                MyTypedStringValue myTypedStringValue = (MyTypedStringValue) value;
                String stringValue = myTypedStringValue.getValue();
                Class<?> targetType = myTypedStringValue.getTargetType();
                if(targetType == Integer.class){
                    valueToUse = Integer.parseInt(stringValue);
                }else if(targetType == String.class){
                    valueToUse = String.valueOf(stringValue);
                }else{

                }
            }else if(value instanceof MyRuntimeBeanReference){
                MyRuntimeBeanReference myRuntimeBeanReference = (MyRuntimeBeanReference) value;
                valueToUse = getBean(myRuntimeBeanReference.getRef());
            }
            ReflectUtils.setProperty(object, name, valueToUse);
        }
    }

}

  最後一個建立的就是功能的集大成者:MyDefaultListableBeanFactory,其實現MyBeanDefinitionRegistory介面,同時整合上一個類MyAbstructAutowireCapableBeanFactory,由於繼承了MyBeanDefinitionRegistory介面,因此就需要實現對應的Beandefinition的註冊和獲取方法。

public class MyDefaultListableBeanFactory extends MyAbstructAutowireCapableBeanFactory implements MyBeanDefinitionRegisty {


    private Map<String, MyBeanDefinition> beanDefinitions = new HashedMap();

    @Override
    public void registry(String beanName, MyBeanDefinition beanDefinition) {
        beanDefinitions.put(beanName, beanDefinition);
    }

    @Override
    public MyBeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitions.get(beanName);
    }

    @Override
    public List<String> getBeanNamesByType(Class<?> type) {
        return null;
    }

    @Override
    public <T> List<T> getBeansByType(Class<?> type) {
        return null;
    }
}

  整體碼完。。。。。。。

附錄、工具類程式碼附錄

  接下來,附上對應的工具類內容

  1、主入口:

@Slf4j
@Data
public class SpringIocFrame {

    public MyDefaultListableBeanFactory init(){
        String location = "write-frame-beans.xml";

        //載入配置檔案
        MyResources resources = new MyClassPathResource(location);

        //建立BeanFactory
        MyDefaultListableBeanFactory beanFactory = new MyDefaultListableBeanFactory();

        //
        MyXmlBeanDefinitionReader reader = new MyXmlBeanDefinitionReader(beanFactory);

        reader.loadBeanDefinitions(resources);

        return beanFactory;
    }

    public void findUserById(String id, MyDefaultListableBeanFactory beanFactory) throws Exception {
        UserService userService = (UserService) beanFactory.getBean("userService");
        UserDo userDo = userService.findUserById(id);
        log.info("=================={}=================", userDo);
    }


}

  2、reader:

package com.lcl.galaxy.spring.frame.V3.reader;

import com.lcl.galaxy.spring.frame.V2.domain.MyBeanDefinition;
import com.lcl.galaxy.spring.frame.V2.domain.MyPropertyValue;
import com.lcl.galaxy.spring.frame.V2.domain.MyRuntimeBeanReference;
import com.lcl.galaxy.spring.frame.V2.domain.MyTypedStringValue;
import com.lcl.galaxy.spring.frame.V3.register.MyBeanDefinitionRegisty;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;

import java.lang.reflect.Field;
import java.util.List;

@Slf4j
public class MyXmlBeanDefinitionDocumentReader {
    
    private MyBeanDefinitionRegisty registy;
    
    public MyXmlBeanDefinitionDocumentReader(MyBeanDefinitionRegisty registy) {
        super();
        this.registy = registy;
    }

    /**
     * 按照spring標籤語義進行解析
     * @param rootElement
     */
    public void loadBeanDefinitions(Element rootElement) {
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            String name = element.getName();
            if ("bean".equals(name)) {
                parseDefaultElement(element);
            } else {
                parseCustomElement(element);
            }
        }
    }


    /**
     * 解析自定義標籤
     * @param element
     */
    private void parseCustomElement(Element element) {
    }

    /**
     * 解析bean標籤,封裝為Bedefinition,並將BeanDefinition放入map中
     * @param beanElement
     */
    private void parseDefaultElement(Element beanElement) {
        if (beanElement == null) {
            return;
        }

        String id = beanElement.attributeValue("id");
        String name = beanElement.attributeValue("name");
        String clazzName = beanElement.attributeValue("class");
        if (clazzName == null || "".equals(clazzName)) {
            return;
        }

        String initMethod = beanElement.attributeValue("initMethod");
        String scope = beanElement.attributeValue("scope");
        scope = scope != null ? scope : "singleton";
        String beanName = id == null ? name : id;
        Class<?> clazzType = null;
        try {
            clazzType = Class.forName(clazzName);
            beanName = beanName == null ? clazzType.getName() : beanName;
            MyBeanDefinition beanDefinition = new MyBeanDefinition(clazzName, beanName);
            beanDefinition.setInitMethod(initMethod);
            beanDefinition.setScope(scope);
            List<Element> propertyElements = beanElement.elements();
            for (Element propertyElement : propertyElements) {
                parsePropertyElement(beanDefinition, propertyElement);
            }
            this.registy.registry(beanName, beanDefinition);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 為BeanDefinition的property集合賦值
     * @param beanDefinition
     * @param propertyElement
     */
    private void parsePropertyElement(MyBeanDefinition beanDefinition, Element propertyElement) {
        String name = propertyElement.attributeValue("name");
        String value = propertyElement.attributeValue("value");
        String ref = propertyElement.attributeValue("ref");

        if(value != null && !"".equals(value) && ref != null && !"".equals(ref)){
            return;
        }

        MyPropertyValue pv = null;
        if(value != null && !"".equals(value)){
            MyTypedStringValue typedStringValue = new MyTypedStringValue(value);
            Class<?> targetType = getTypeFieldName(beanDefinition.getClazzName(), name);
            typedStringValue.setTargetType(targetType);
            pv = new MyPropertyValue(name, typedStringValue);
            beanDefinition.addPropertyValue(pv);
        }else{
            MyRuntimeBeanReference runtimeBeanReference = new MyRuntimeBeanReference(ref);
            pv = new MyPropertyValue(name, runtimeBeanReference);
            beanDefinition.addPropertyValue(pv);
        }
    }

    /**
     * 獲取簡單型別
     * @param clazzName
     * @param name
     * @return
     */
    private Class<?> getTypeFieldName(String clazzName, String name) {
        try {
            Class<?> clazz = Class.forName(clazzName);
            Field field = clazz.getDeclaredField(name);
            return field.getType();
        } catch (Exception e) {
            log.info("================== clazzName,name ====================,{}==========,{}",clazzName, name);
            e.printStackTrace();
        }
        return null;
    }
}
package com.lcl.galaxy.spring.frame.V3.reader;

import com.lcl.galaxy.spring.frame.V3.register.MyBeanDefinitionRegisty;
import com.lcl.galaxy.spring.frame.V3.resource.MyResources;
import com.lcl.galaxy.spring.frame.V3.utils.DocumentUtils;
import org.dom4j.Document;

import java.io.InputStream;

public class MyXmlBeanDefinitionReader {

    private MyBeanDefinitionRegisty registy;

    public MyXmlBeanDefinitionReader(MyBeanDefinitionRegisty registy) {
        this.registy = registy;
    }

    public void loadBeanDefinitions(MyResources resources) {
        InputStream inputStream = resources.getResourceAsStream();
        Document document = DocumentUtils.readInputStream(inputStream);
        MyXmlBeanDefinitionDocumentReader xmlBeanDefinitionDocumentReader = new MyXmlBeanDefinitionDocumentReader(registy);
        xmlBeanDefinitionDocumentReader.loadBeanDefinitions(document.getRootElement());
    }
}

  3、resource

public class MyClassPathResource implements MyResources {

    private String location;

    public MyClassPathResource(String location){
        this.location = location;
    }

    @Override
    public InputStream getResourceAsStream() {
        return MyResources.class.getClassLoader().getResourceAsStream(location);
    }
}
public interface MyResources {
    InputStream getResourceAsStream();
}

  4、utils

public class DocumentUtils {
    public static Document readInputStream(InputStream inputStream) {
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            return document;
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class ReflectUtils {

    /**
     * 通過反射獲取物件
     * @param args
     * @return
     */
    public static Object createObject(Class<?> clazz, Object... args){
        try {
            Constructor<?> constructor = clazz.getConstructor();
            return constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通過反射設定屬性值
     * @param object
     * @param name
     * @param valueToUse
     */
    public static void setProperty(Object object, String name, Object valueToUse){
        Class<?> clazz = object.getClass();
        try {
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            field.set(object, valueToUse);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通過反射獲取欄位的型別
     * @param beanClassName
     * @param name
     * @return
     */
    public static Class<?> getTypeByFieldName(String beanClassName, String name){
        try {
            Class<?> clazz = Class.forName(beanClassName);
            Field field = clazz.getDeclaredField(name);
            return field.getType();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通過反射執行init方法
     * @param object
     * @param initMethod
     */
    public static void invokeMethod(Object object, String initMethod){
        Class<?> clazz = object.getClass();
        try {
            Method declaredMethod = clazz.getDeclaredMethod(initMethod);
            declaredMethod.setAccessible(true);
            declaredMethod.invoke(initMethod);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三、總結

  對於IoC框架的手寫,其實V2版本已經實現了功能,V3版本只是借鑑Spring原始碼對V2版本進行了梳理,程式碼邏輯沒有變化,只是對類的整合體系、設計模式等做了相應的調整,其實手寫完V3版本,就已經大致瞭解Spring原始碼中相關類的繼承體系,對於啃Spring Ioc的原始碼有很大的幫助,