手写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:实现了aware接口的类,就可以获取BeanFactory对象

  其中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<?> clazz) {
        List<T> list = new ArrayList<>(beanDefinitions.size());
        for(MyBeanDefinition beanDefinition : beanDefinitions.values()){
            String clazzName = beanDefinition.getClazzName();
            Class<?> type = resolveClassName(clazzName);
            if(clazz.isAssignableFrom(type)){
                Object bean = getBean(beanDefinition.getBeanName());
                list.add((T) bean);
            }
        }
        return list;
    }

    private Class<?> resolveClassName(String clazzName) {
        try {
            return Class.forName(clazzName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        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(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三、总结

  对于IoC框架的手写,其实V2版本已经实现了功能,V3版本只是借鉴Spring源码对V2版本进行了梳理,代码逻辑没有变化,只是对类的集成体系、设计模式等做了相应的调整,其实手写完V3版本,就已经大致了解Spring源码中相关类的继承体系,对于啃Spring Ioc的源码有很大的帮助,

 

posted @ 2020-12-24 21:42  李聪龙  阅读(347)  评论(0编辑  收藏  举报