手写轻量级Spring IOC框架

前言:

        本博客将完全通过自己手写的代码来实现基于XML配置方式的SpringIOC轻量级框架,主要是对自己学习tiny_spring项目的记录和总结。本人水平有限,难免存在不足和错误,还请大家多多指正。

一、Spring IOC概述

        IOC,即控制反转。不是一种技术,而是 Spring 的一种设计思想。

        在传统的程序设计中,我们直接在对象内部通过 new XXX() 来创建对象,是程序主动去创建依赖的对象;而在Spring中提供了一种IOC容器,用来控制对象的创建,无论是创建对象,还是处理对象之间的依赖关系,都由该容器进行统一管理。

        所有类的创建,销毁都由Spring来控制,即控制对象生命周期的不是引用该对象的对象,而是Spring容器。对于某个具体的对象而言,以前是它控制其他对象,现在是所有的对象都被Spring容器控制,这叫控制反转。

        下面通过两张图来理解Spring IOC

       

 二、项目结构图

        

 

 三、具体实现

        (1)定义配置资源文件操作相关的类,用于解决IOC容器中的内容从哪里来的问题,也就是配置文件从哪里读取、配置文件如何读取的问题。

/**
 * Resource 接口,标识一个外部资源。通过 getInputStream() 方法获取资源的输入流。
 */
public interface Resource {

    InputStream getInputStream() throws IOException;
}

/**
 * 实现 Resource 接口的资源类,通过 URL 获取资源
 */
public class URLResource implements Resource {

    private URL url;

    public URLResource(URL url) {
        this.url = url;
    }

    public InputStream getInputStream() throws IOException {
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();
        return urlConnection.getInputStream();
    }
}
/**
 * 资源加载接口,通过 getResource(String) 方法获取一个 Resouce 对象,
   是获取 Resource 的主要途径
 */
public interface ResourceLoader {

    Resource getResource(String config);
}

/**
 * 实现 ResourceLoader 接口,获取 URLResource 对象
 */
public class UrlResourceLoader implements ResourceLoader {

    public Resource getResource(String config) {
        URL url = this.getClass().getClassLoader().getResource(config);
        return new URLResource(url);
    }
}

        (2)定义了以 BeanDefinition 类为核心发散的几个类,用于解决 Bean的具体定义,包括 Bean的名字,类型,属性(值或引用)。

/**
 * Bean 定义
 * (1) 包括 Bean 的名字 beanName,Bean 的类型 beanClass,Bean 的属性集合 beanPropertys
 */
public class BeanDefinition {

    private String beanName;

    private String beanClass;

    private BeanPropertys beanPropertys;

    private Object bean;

    public BeanDefinition(String beanName, String beanClass) {
        this.beanName = beanName;
        this.beanClass = beanClass;
    }

    public void setBeanPropertys(BeanPropertys beanPropertys) {
        this.beanPropertys = beanPropertys;
    }

    public String getBeanName() {
        return beanName;
    }

    public Object getBean() {
        return bean;
    }

    public String getBeanClass() {
        return beanClass;
    }

    public BeanPropertys getBeanPropertys() {
        return beanPropertys;
    }
}

/**
 * 定义 Bean 属性集合,包含 beanPropertys 变量,里面包含一个个PropertyValue条目
 */
public class BeanPropertys {

    // bean 属性名集合
    private List<String> propNames = new ArrayList<String>();

    // bean 属性集合
    private List<BeanProperty> beanPropertys = new ArrayList<BeanProperty>();

    /**
     * 增加 BeanProperty对象(Bean属性)到 Bean 属性集合
     * 如果 属性集合已经存在该 Bean 属性名,则不再次添加
     * @param beanProperty
     */
    public void addBeanProperty(BeanProperty beanProperty) {
        String propName = beanProperty.getPropName();
        if (beanPropertys.contains(propName)) {
            return;
        }
        beanPropertys.add(beanProperty);
        propNames.add(propName);
    }

    public boolean isEmpty() {
        return beanPropertys.isEmpty();
    }

    public List<BeanProperty> getBeanPropertys() {
        return beanPropertys;
    }
}

/**
 * 定义 Bean属性 类
 * 每个对象都是键值对 String-Object,分别对应要生成示例的属性的名字与类型。
 * 在Spring的XML中的 property中,键是 key,值是 value 或者 ref。
 * 用来定义xml配置文件中的如下节点:
 *  <property name="desc" value="I am Print Service"></property>
 * or
 *  <property name="helloWorldService" ref="helloWorldService"></property>
 */
public class BeanProperty {

    // 属性名称
    private String propName;

    public String getPropName() {
        return propName;
    }

    // 属性值或属性引用
    private Object propValue;

    public Object getPropValue() {
        return propValue;
    }

    public BeanProperty(String propName, Object propValue) {
        this.propName = propName;
        this.propValue = propValue;
    }
}

/**
 * 用来定义Bean的引用,其中保存了Bean的名字,
 * 需要用的时候先进行解析beanRef,再根据值获取Bean对象
 */
public class BeanReference {

    private String beanRef;

    public BeanReference(String beanRef) {
        this.beanRef = beanRef;
    }

    public String getBeanRef() {
        return beanRef;
    }
}

           (3)定义以实现 BeanDefinitionReader 核心接口的几个类,主要用于解析配置文件,构建 BeanDefinition。

/**
 * @Desc 解析 BeanDefinition 的接口
 * @author xiongxl
 * @Date 2022/7/25
 */
public interface BeanDefinitionReader {

    /**
     * 通过 loadBeanDefinitions(String) 来从一个地址加载类定义
     * @param location
     */
    void loadBeanDefinitions(String location);
}

/**
 * @Desc 实现 BeanDefinitionReader 接口的抽象类(未具体实现 loadBeanDefinitions,而是规范了 BeanDefinitionReader 的基本结构)
 * @author xiongxl
 * @Date 2022/7/25
 */
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

    /**
     * BeanDefinitionReader 的基本机构由:
     *      beanDefinitionRegister :用于保存 BeanDefinition
     *      resourceLoader:用于保存资源加载器
     * 用意在于,使用时,只需要向其 loadBeanDefinitions() 传入一个资源地址,就可以自动调用其类加载器,
     * 并把解析到的 BeanDefinition 保存到 beanDefinitionRegister 中去。
     *
     */

    private List<BeanDefinition> beanDefinitionRegister;

    private ResourceLoader resourceLoader;

    public AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
        beanDefinitionRegister = new ArrayList<BeanDefinition>();
    }

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

    public List<BeanDefinition> getBeanDefinitionRegister() {
        return beanDefinitionRegister;
    }
}

/**
 * @Desc 继承 AbstractBeanDefinitionReader,实现了 loadBeanDefinitions() 方法,从 XML 文件中读取类定义。
 * @author xiongxl
 * @Date 2022/7/25
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }

    /**
     * 解析XML资源文件,读取类定义BeanDefinition,存储到 beanDefinitionRegister
     * @param location
     */
    public void loadBeanDefinitions(String location) {
        // 根据Spring资源文件获取 URLResource
        Resource resource = getResourceLoader().getResource(location);
        try {
            // 获取文件 InputStream
            InputStream inputStream = resource.getInputStream();
            parseInputStream(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析Spring配置文件的InputStream
     * @param inputStream
     * @throws Exception
     */
    private void parseInputStream(InputStream inputStream) throws Exception {
        // 获取 DocumentBuildFactory 对象
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        // 获取 DocumentBuilder 对象
        DocumentBuilder db = dbf.newDocumentBuilder();
        // 调用 DocumentBuilder 对象的 parse 方法 获取 Document 对象
        Document document = db.parse(inputStream);
        parseDocument(document);
    }

    /**
     * 解析Spring配置文件的Document对象
     * @param document
     */
    private void parseDocument(Document document) {
        // 获取 Document对象 的根节点
        Element rootEle = document.getDocumentElement();
        // 获取所有的子节点
        NodeList nodeList = rootEle.getChildNodes();
        if (nodeList.getLength() > 0) {
            for (int i=0; i< nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                if (node instanceof Element) {
                    Element beanEle = (Element) node;
                    // <bean name="helloWorldService" class="study.tinyioc.service.HelloWorldService">
                    // 获取Bean节点的name属性值
                    String beanName = beanEle.getAttribute("id");
                    // 获取Bean节点的class属性值
                    String beanClass = beanEle.getAttribute("class");
                    // 构建 BeanDefinition
                    BeanDefinition bd = new BeanDefinition(beanName, beanClass);
                    // 获取 Bean 节点的子节点,即 Bean 的属性集合节点节点集合
                    NodeList beanPropertyNodes = beanEle.getElementsByTagName("property");
                    if (beanPropertyNodes.getLength() > 0) {
                        BeanPropertys beanPropertys = buildBeanPropertys(beanPropertyNodes);
                        if (!beanPropertys.isEmpty()) {
                            bd.setBeanPropertys(beanPropertys);
                        }
                    }
                    getBeanDefinitionRegister().add(bd);
                }
            }
        }
    }

    /**
     * 解析Bean的成员变量节点集合
     * @param beanPropNodeList
     * @return
     */
    private BeanPropertys buildBeanPropertys(NodeList beanPropNodeList) {
        BeanPropertys beanPropertys = new BeanPropertys();
        for (int i=0; i<beanPropNodeList.getLength(); i++) {
            Node propNode = beanPropNodeList.item(i);
            if (propNode instanceof Element) {
                Element propEle = (Element) propNode;
                // <property name="city" value="JiangNing"></property> OR <property name="helloWorldService" ref="helloWorldService"></property>
                // 获取 Bean 的属性名
                String pn = propEle.getAttribute("name");
                if (pn == null || pn.length() == 0) {
                    throw new IllegalArgumentException("There id an error in Spring configuration. " +
                            "There is a property node without name");
                }
                // 获取 Bean 的属性值
                String pv = propEle.getAttribute("value");
                if (pv != null && pv.length() > 0) {
                    // 如果 value 值存在,则构建 BeanProperty
                    BeanProperty bp = new BeanProperty(pn, pv);
                    beanPropertys.addBeanProperty(bp);
                } else {
                    // 如果 value 值不存在,则尝试获取 ref 值
                    String propRef = propEle.getAttribute("ref");
                    if (propRef != null && propRef.length() > 0) {
                        // 如果 ref 值存在,则构建 BeanReference
                        BeanReference bf = new BeanReference(propRef);
                        BeanProperty bp = new BeanProperty(pn, bf);
                        beanPropertys.addBeanProperty(bp);
                    } else {
                       throw new IllegalArgumentException("Configuration problean :<property> element for " +
                               "property [" + pn + "] must specify a ref or value");
                    }
                }
            }
        }
        return beanPropertys;
    }
}

        (4)定义以 BeanFactory 接口为核心发散出的几个类,用于解决IOC容器在已经获取 Bean 的定义(BeanDefinition)的情况下,如何装配、获取 Bean 实例的问题。

/**
 * @Desc Bean工厂接口,标识一个 IOC 容器。通过 getBean(String) 方法来 获取一个对象
 * @author xiongxl
 * @Date 2022/7/26
 */
public interface BeanFactory {

    /**
     * 根据 Bean Name 从 IOC容器 获取Bean实体
     * @param beanName
     * @return
     */
    Object getBean(String beanName);

    /**
     * 注册 BeanDefinition
     * @param beanDefinition
     */
    void registerBeanDefinition(BeanDefinition beanDefinition);
}

/**
 * @Desc BeanFactory 的一种抽象类表现,规范了IOC容器的基本结构。
 * @author xiongxl
 * @Date 2022/7/26
 */
public abstract class AbstractBeanFactory implements BeanFactory {

    /**
     * beanDefinitionMap 哈希表用于保存类的定义信息(BeanDefinition)。
     */
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    /**
     * 根据 beanName 从 beanDefinitionMap 获取Bean,如果 Bean 已经存在于容器中,则返回即可,
     * 如果 Bean 不存在于容器中,则调用 createBean 方法装配一个 Bean
     * 注:所谓存在于容器中,是指容器可以通过 beanDefinitionMap 获取 BeanDefinition 进而通过其 getBean() 方法获取 Bean实例
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new RuntimeException("No Found Bean Named:[" + beanName + "]");
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Object bean = beanDefinition.getBean();
        if (bean == null) {
            bean = createBean(beanDefinition);
        }
        return bean;
    }

    public void registerBeanDefinition(BeanDefinition beanDefinition) {
        String beanName = beanDefinition.getBeanName();
        if (beanDefinitionMap.containsKey(beanName)) {
            return;
        }
        beanDefinitionMap.put(beanName, beanDefinition);
    }

    protected abstract Object createBean(BeanDefinition beanDefinition);
}

/**
 * @Desc 可以实现自动装配的 BeanFactory
 * @author xiongxl
 * @Date 2022/7/26
 */
public class AutowiredCapableBeanFactory extends AbstractBeanFactory {

    protected Object createBean(BeanDefinition beanDefinition) {
        Object bean = null;
        String beanClass = beanDefinition.getBeanClass();
        try {
            // 通过 Bean定义(BeanDefinition)实例化出 Bean 对象
            bean = Class.forName(beanClass).newInstance();
            BeanPropertys beanPropertys = beanDefinition.getBeanPropertys();
            if (!beanPropertys.isEmpty()) {
                loadBeanPropertys(bean, beanPropertys);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 把对象保存在 BeanDefinition 中,以备下次获取
        beanDefinition.setBean(bean);
        return bean;
    }

    /**
     * 装配 Bean 实例的属性
     * @param bean
     * @param beanPropertys
     * @throws Exception
     */
    private void loadBeanPropertys(Object bean, BeanPropertys beanPropertys) throws Exception {
        // 从 Bean定义(BeanDefinition)中获取 Bean 属性集合
        List<BeanProperty> beanPropertyList = beanPropertys.getBeanPropertys();
        // 遍历 Bean 属性集合
        for (BeanProperty beanProperty : beanPropertyList) {
            // 获取 Bean 属性名
            String propName = beanProperty.getPropName();
            // 获取 Bean 属性值
            Object propValue = beanProperty.getPropValue();
            /**
             * 通过反射,把 String - Value 键值对注入到 Bean 的属性中去。
             * 如果 Value 的类型是 BeanReference,则说明其是一个引用(对应于 XML 中的 ref),通过 getBean 对其进行获取,然后注入到属性中。
             */
            Field field = bean.getClass().getDeclaredField(propName);
            field.setAccessible(true);
            if (propValue instanceof BeanReference) {
                BeanReference beanReference = (BeanReference)propValue;
                String beanRef = beanReference.getBeanRef();
                Object refBean = getBean(beanRef);
                field.set(bean, refBean);
            } else {
                field.set(bean, propValue);
            }
        }
    }
}

        (5)定义了以 ApplicationContext 接口为核心发散出的几个类,主要是对前面 Resource、BeanFactory、BeanDefinition 进行了功能的封装,解决 根据地址获取IOC容器并使用的问题。

/**
 * @Desc 标记接口,继承了 BeanFactory。通常,要实现一个IOC容器时,需要先通过 ResourceLoader 获取一个 Resource,其中包括了容器的配置、Bean的定义信息。
 *       接着,使用 BeanDefinitionReader 读取该 Resource 中的 BeanDefinition 信息。最后,把 BeanDefinition 保存在 BeanFactory 中,容器配置完毕可以使用。
 *       注意到 BeanFactory 只是实现了 Bean 的装配、获取,并未说明 Bean 的来源 也就是 BeanDefinition 是如何加载的。该接口把 BeanFactory 和 BeanDefinitionReader 结合在一起了。
 * @author xiongxl
 * @Date 2022/7/27
 */
public interface ApplicationContext extends BeanFactory {

}

/**
 * @Desc ApplicationContext 的抽象实现,内部包含一个 BeanFactory 类。主要方法有 getBean() 和 refresh() 方法。
 * @author xiongxl
 * @Date 2022/7/27
 */
public abstract class AbstractApplicationContext implements ApplicationContext {

    protected AbstractBeanFactory beanFactory;

    public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * refresh() 则用于实现 BeanFactory 的刷新,也就是告诉 BeanFactory 该使用哪个资源(Resource)加载类定义(BeanDefinition)信息
     * 该方法留给了子类实现,用以实现从不通来源的不同类型的资源加载类定义的效果
     */
    protected abstract void refresh();

    /**
     * getBean() 直接调用了内置 BeanFactory 的 getBean() 方法
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return beanFactory.getBean(beanName);
    }
}

/**
 * @Desc 从类路径加载资源的具体实现类。
 * @author xiongxl
 * @Date 2022/7/27
 */
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

    private String springConfig;

    public ClassPathXmlApplicationContext(String springConfig) {
        super(new AutowiredCapableBeanFactory());
        this.springConfig = springConfig;
        refresh();
    }

    /**
     * 通过 XmlBeanDefinitionReader 解析 UrlResourceLoader 读取到 Resource,获取 BeanDefinition 信息,然后将其保存在内置的 BeanFactory 中。
     */
    @Override
    protected void refresh() {
        ResourceLoader resourceLoader = new UrlResourceLoader();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(resourceLoader);
        xmlBeanDefinitionReader.loadBeanDefinitions(this.springConfig);
        List<BeanDefinition> beanDefinitionList = xmlBeanDefinitionReader.getBeanDefinitionRegister();
        for (BeanDefinition beanDefinition : beanDefinitionList) {
            beanFactory.registerBeanDefinition(beanDefinition);
        }
    }
}

        (6)Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <bean id="helloWorldService" class="study.tinyioc.service.HelloWorldService">
        <property name="city" value="Nanjing"></property>
        <property name="desc" value="我爱南京"></property>
    </bean>

    <bean id="printService" class="study.tinyioc.service.PrintService">
        <property name="desc" value="I am Print Service"></property>
        <property name="helloWorldService" ref="helloWorldService"></property>
    </bean>

</beans>

        (7)Service类

public class HelloWorldService {

    private String city;

    private String desc;

    public void setCity(String city) {
        this.city = city;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public void say() {
        System.out.println("城市:[" + city + "] " + desc);
    }
}

public class PrintService {

    private String desc;

    private HelloWorldService helloWorldService;

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public void setHelloWorldService(HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }

    public void print() {
        System.out.println(desc);
        helloWorldService.say();
    }
}

        (8)测试类

@Test
    public void testContext() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
        PrintService printService = (PrintService) applicationContext.getBean("printService");
        printService.print();
        HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
        helloWorldService.say();
    }

        源码下载:git@gitee.com:xiong-xianliang/study-tiny-spring.git

        由于本人水平有限,本博客可能存在不足和错误在所难免,还请大家多多指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值