spring知识汲取(汲取自各大神,如有侵权,望见谅)

  • spring是为开发者提供的一站式轻量级应用开发平台,让开发人员更关注于自己的业务实现。
  • 核心功能:
  1. 通过IOC容器实现对象耦合关系的管理。轻松解耦,实现了控制反转。
  2. 通过AOP以动态非侵入的方式增强服务
  • IOC

ObjA依赖ObjB、ObjC、ObjD。spring之前我们都是这样实现的(麻烦不麻烦,很麻烦,而且耦合度特别高):

class ObjA{
    public ObjB objB;
    public ObjC objC;
    public ObjD objD;

    public ObjA(ObjB objB, ObjC objC, ObjD objD) {
        this.objB = objB;
        this.objC = objC;
        this.objD = objD;
    }
}
使用时主动创建:
        ObjB objB = new ObjB();
        ObjC objC = new ObjC();
        ObjD objD = new ObjD();
        ObjA objA = new ObjA(objB,objC,objD);

IOC如何实现的?看下面(其实我感觉ioc容器实现起来,获取方式简单了,但是配置复杂呀……,不过也挺好的)

获取ObjA,直接通过反射从ioc就可以取回来做过正确依赖注入的ObjA。

xml定义:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id="objB" class="ObjB"> 
   <bean id="objC" class="ObjC">
   <bean id="objD" class="ObjD">
   </bean>
   <bean id="objA" class="ObjA"> 
       <property name="objB"> 
           <ref bean="objB"></ref>
       </property>
       <property name="objC"> 
           <ref bean="objC"></ref>
       </property>
       <property name="objD"> 
           <ref bean="objD"></ref>
       </property>
   </bean>
</beans>

使用时:
BeanFactory factory=new XmlBeanFactory("bean.xml");
ObjA a =(ObjA)factory.getBean("objA");

自己实现ioc咋实现?

组件设计

我们基于上面的话来看看,我们需要什么要组件,首先得有BeanFactory IOC容器嘛,然后得有BeanDefinition  当然还需要加载Bean的资源加载器

容器用来存放初始化好的Bean,BeanDefinition 就是Bean的基本数据结构,比如Bean的名称,Bean的属性 PropertyValue,Bean的方法,是否延迟加载,依赖关系等。资源加载器就简单了,就是一个读取XML配置文件的类,读取每个标签并解析。

设计接口

BeanFactory

首先肯定需要一个BeanFactory,就是Bean容器,容器接口至少有2个最简单的方法,一个是获取Bean,一个注册Bean

package com.liuliu.ioc;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:20
 * 需要一个beanFactory 定义ioc 容器的一些行为 比如根据名称获取bean, 比如注册bean,参数为bean的名称,bean的定义
 */
public interface BeanFactory {

    /**
     * 根据bean的名称从容器中获取bean对象
     *
     * @param name bean 名称
     * @return bean实例
     * @throws Exception 异常
     */
    Object getBean(String name) throws Exception;

    /**
     * 将bean注册到容器中
     *
     * @param name bean 名称
     * @param bean bean实例
     * @throws Exception 异常
     */
    void registerBeanDefinition(String name, BeanDefinition bean) throws Exception;
}

BeanDefinition

定义完了Bean最基本的容器,还需要一个最简单 BeanDefinition 接口,我们为了方便,但因为我们这个不必考虑扩展,因此可以直接设计为类,BeanDefinition 需要哪些元素和方法呢?需要一个 Bean 对象,一个Class对象,一个ClassName字符串,还需要一个元素集合 PropertyValues。这些就能组成一个最基本的 BeanDefinition 类了。那么需要哪些方法呢?其实就是这些属性的get set 方法。我们看看该类的详细:

package com.liuliu.ioc;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:21
 */
public class BeanDefinition {
    /**
     * bean
     */
    private Object bean;

    /**
     * bean 的 CLass 对象
     */
    private Class beanClass;

    /**
     * bean 的类全限定名称
     */
    private String ClassName;

    /**
     * 类的属性集合
     */
    private PropertyValues propertyValues = new PropertyValues();

    /**
     * 获取bean对象
     */
    public Object getBean() {
        return this.bean;
    }

    /**
     * 设置bean的对象
     */
    public void setBean(Object bean) {
        this.bean = bean;
    }

    /**
     * 获取bean的Class对象
     */
    public Class getBeanclass() {
        return this.beanClass;
    }

    /**
     * 通过设置类名称反射生成Class对象
     */
    public void setClassname(String name) {
        this.ClassName = name;
        try {
            this.beanClass = Class.forName(name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取bean的属性集合
     */
    public PropertyValues getPropertyValues() {
        return this.propertyValues;
    }

    /**
     * 设置bean的属性
     */
    public void setPropertyValues(PropertyValues pv) {
        this.propertyValues = pv;
    }

}

BeanDefinitionReader

有了基本的 BeanDefinition 数据结构,还需要一个从XML中读取并解析为 BeanDefinition 的操作类,首先我们定义一个 BeanDefinitionReader 接口,该接口只是一个标识,具体由抽象类去实现一个基本方法和定义一些基本属性,比如一个读取时需要存放的注册容器,还需要一个委托一个资源加载器 ResourceLoader, 用于加载XML文件,并且我们需要设置该构造器必须含有资源加载器,当然还有一些get set 方法。

  • AbstractBeanDefinitionReader
package com.liuliu.ioc;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:44
 */
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

    /**
     * 注册bean容器
     */
    private Map registry;
    /**
     * 资源加载器
     */
    private ResourceLoader resourceLoader;
    /**
     * 构造器器必须有一个资源加载器, 默认插件创建一个map容器
     *
     * @param resourceLoader 资源加载器
     */
    protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
        this.registry = new HashMap<>();
        this.resourceLoader = resourceLoader;
    }
    /**
     * 获取容器
     */
    public Map getRegistry() {return registry;
    }
    /**
     * 获取资源加载器
     */
    public ResourceLoader getResourceLoader() {return resourceLoader;
    }
}
  • BeanDefinitionReader 预留接口
package com.liuliu.ioc;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:44
 * 读取bean定义的接口
 */
public interface BeanDefinitionReader {
}

-xml 读取相关的类 - Resource

package com.liuliu.ioc;

/**
* @author 小六六
* @version 1.0
* @date 2020/9/24 16:46
*/

import java.io.InputStream;

/**
* 资源定义
*
* @author stateis0
*/
public interface Resource {

   /**
    * 获取输入流
    */
   InputStream getInputstream() throws Exception;
}

package com.liuliu.ioc;

import java.net.URL;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:46
 */
public class ResourceLoader {

    /**
     * 给定一个位置, 使用累加器的资源加载URL,并创建一个“资源URL”对象,便于获取输入流
     */
    public ResourceUrl getResource(String location) {
        URL url = this.getClass().getClassLoader().getResource(location);
        return new ResourceUrl(url);
    }
}

package com.liuliu.ioc;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:46
 */

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * 资源URL
 */
public class ResourceUrl implements Resource {

    /**
     * 类库URL
     */
    private final URL url;

    /**
     * 需要一个类库URL
     */
    public ResourceUrl(URL url) {
        this.url = url;
    }

    /**
     * 从URL中获取输入流
     */
    @Override
    public InputStream getInputstream() throws Exception {
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();
        return urlConnection.getInputStream();

    }

}

XmlBeanDefinitionReader

好了, AbstractBeanDefinitionReader 需要的元素已经有了, 但是,很明显该方法不能实现读取 BeanDefinition 的任务。那么我们需要一个类去继承抽象类,去实现具体的方法, 既然我们是XML 配置文件读取,那么我们就定义一个 XmlBeanDefinitionReader 继承 AbstractBeanDefinitionReader ,实现一些我们需要的方法, 比如读取XML 的readrXML, 比如将解析出来的元素注册到 registry 的 Map 中, 一些解析的细节。我们还是看代码吧。

package com.liuliu.ioc;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:55
 * 解析XML文件
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    /**
     * 构造器,必须包含一个资源加载器
     *
     * @param resourceLoader 资源加载器
     */
    public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }

    public void readerXML(String location) throws Exception {
        // 创建一个资源加载器
        ResourceLoader resourceloader = new ResourceLoader();
        // 从资源加载器中获取输入流
        InputStream inputstream = resourceloader.getResource(location).getInputstream();
        // 获取文档建造者工厂实例
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 工厂创建文档建造者
        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        // 文档建造者解析流 返回文档对象
        Document doc = docBuilder.parse(inputstream);
        // 根据给定的文档对象进行解析,并注册到bean容器中
        registerBeanDefinitions(doc);
        // 关闭流
        inputstream.close();
    }

    /**
     * 根据给定的文档对象进行解析,并注册到bean容器中
     *
     * @param doc 文档对象
     */
    private void registerBeanDefinitions(Document doc) {
        // 读取文档的根元素
        Element root = doc.getDocumentElement();
        // 解析元素的根节点及根节点下的所有子节点并添加进注册容器
        parseBeanDefinitions(root);
    }

    /**
     * 解析元素的根节点及根节点下的所有子节点并添加进注册容器
     *
     * @param root XML 文件根节点
     */
    private void parseBeanDefinitions(Element root) {
        // 读取根元素的所有子元素
        NodeList nl = root.getChildNodes();
        // 遍历子元素
        for (int i = 0; i             // 获取根元素的给定位置的节点
            Node node = nl.item(i);
            // 类型判断
            if (node instanceof Element) {
                // 强转为父类型元素
                Element ele = (Element) node;
                // 解析给给定的节点,包括name,class,property, name, value,ref
                processBeanDefinition(ele);
            }
        }
    }

    /**
     * 解析给给定的节点,包括name,class,property, name, value,ref
     *
     * @param ele XML 解析元素
     */
    private void processBeanDefinition(Element ele) {
        // 获取给定元素的 name 属性
        String name = ele.getAttribute("name");
        // 获取给定元素的 class 属性
        String className = ele.getAttribute("class");
        // 创建一个bean定义对象
        BeanDefinition beanDefinition = new BeanDefinition();
        // 设置bean 定义对象的 全限定类名
        beanDefinition.setClassname(className);
        // 向 bean 注入配置文件中的成员变量
        addPropertyValues(ele, beanDefinition);
        // 向注册容器 添加bean名称和bean定义
        getRegistry().put(name, beanDefinition);
    }

    /**
     * 添加配置文件中的属性元素到bean定义实例中
     *
     * @param ele 元素
     * @param beandefinition bean定义 对象
     */
    private void addPropertyValues(Element ele, BeanDefinition beandefinition) {
        // 获取给定元素的 property 属性集合
        NodeList propertyNode = ele.getElementsByTagName("property");
        // 循环集合
        for (int i = 0; i             // 获取集合中某个给定位置的节点
            Node node = propertyNode.item(i);
            // 类型判断
            if (node instanceof Element) {
                // 将节点向下强转为子元素
                Element propertyEle = (Element) node;
                // 元素对象获取 name 属性
                String name = propertyEle.getAttribute("name");
                // 元素对象获取 value 属性值
                String value = propertyEle.getAttribute("value");
                // 判断value不为空
                if (value != null && value.length() > 0) {
                    // 向给定的 “bean定义” 实例中添加该成员变量
                    beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
                } else {
                    // 如果为空,则获取属性ref
                    String ref = propertyEle.getAttribute("ref");
                    if (ref == null || ref.length() == 0) {
                        // 如果属性ref为空,则抛出异常
                        throw new IllegalArgumentException(
                                "Configuration problem:  element for property '"
                                        + name + "' must specify a ref or value");
                    }
                    // 如果不为空,测创建一个 “bean的引用” 实例,构造参数为名称,实例暂时为空
                    BeanReference beanRef = new BeanReference(name);
                    // 向给定的 “bean定义” 中添加成员变量
                    beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanRef));
                }
            }
        }
    }

}

可以说代码注释写的非常详细,该类方法如下:

  • public void readerXML(String location) 公开的解析XML的方法,给定一个位置的字符串参数即可。
  • private void registerBeanDefinitions(Document doc) 给定一个文档对象,并进行解析。
  • private void parseBeanDefinitions(Element root) 给定一个根元素,循环解析根元素下所有子元素。
  • private void processBeanDefinition(Element ele) 给定一个子元素,并对元素进行解析,然后拿着解析出来的数据创建一个 BeanDefinition 对象。并注册到BeanDefinitionReader 的 Map 容器(该容器存放着解析时的所有Bean)中。
  • private void addPropertyValues(Element ele, BeanDefinition beandefinition) 给定一个元素,一个 BeanDefinition 对象,解析元素中的 property 元素, 并注入到 BeanDefinition 实例中。一共5步,完成了解析XML文件的所有操作。最终的目的是将解析出来的文件放入到 BeanDefinitionReader 的 Map 容器中。

真正的Bean容器

好了,到这里,我们已经完成了从XML文件读取并解析的步骤,那么什么时候放进BeanFactory的容器呢?刚刚我们只是放进了 AbstractBeanDefinitionReader 的注册容器中。因此我们要根据BeanFactory 的设计来实现如何构建成一个真正能用的Bean呢?因为刚才的哪些Bean只是一些Bean的信息。没有我们真正业务需要的Bean。

package com.liuliu.ioc;

import java.util.HashMap;

/**
 * @author 小六六
 * @version 1.0
 * @date 2020/9/24 16:58
 * 一个抽象类, 实现了 bean 的方法,包含一个map,用于存储bean 的名字和bean的定义
 */
public abstract class AbstractBeanFactory implements BeanFactory {

    /**
     * 容器
     */
    private HashMap map = new HashMap<>();
    /**
     * 根据bean的名称获取bean, 如果没有,则抛出异常 如果有, 则从bean定义对象获取bean实例
     */
    @Override
    public Object getBean(String name) throws Exception {
        BeanDefinition beandefinition = map.get(name);if (beandefinition == null) {
            throw new IllegalArgumentException("No bean named " + name + " is defined");
        }
        Object bean = beandefinition.getBean();if (bean == null) {
            bean = doCreate(beandefinition);
        }return bean;
    }
    /**
     * 注册 bean定义 的抽象方法实现,这是一个模板方法, 调用子类方法doCreate,
     */
    @Override
    public void registerBeanDefinition(String name, BeanDefinition beandefinition) throws Exception {
        Object bean = doCreate(beandefinition);
        beandefinition.setBean(bean);
        map.put(name, beandefinition);
    }
    /**
     * 减少一个bean
     */
    abstract Object doCreate(BeanDefinition beandefinition) throws Exception;
}

该类实现了接口的2个基本方法,一个是getBean,一个是 registerBeanDefinition, 我们也设计了一个抽象方法供这两个方法调用,将具体逻辑创建逻辑延迟到子类。这是什么设计模式呢?模板模式。主要还是看 doCreate 方法,就是创建bean 具体方法,所以我们还是需要一个子类, 叫什么呢? AutowireBeanFactory, 自动注入Bean,这是我们这个标准Bean工厂的工作。看看代码吧?

可以看到 doCreate 方法使用了反射创建了一个对象,并且还需要对该对象进行属性注入,如果属性是 ref 类型,那么既是依赖关系,则需要调用 getBean 方法递归的去寻找那个Bean(因为最后一个Bean 的属性肯定是基本类型)。这样就完成了一次获取实例化Bean操作,并且也实现类依赖注入。

总结

我们通过这些代码实现了一个简单的 IOC 依赖注入的功能,也更加了解了 IOC, 以后遇到Spring初始化的问题再也不会手足无措了。直接看源码就能解决。但是这只是冰山一角,你还得实现包扫描,扫描那些Bean需要先注入到容器中,还有就是Spring 有各种前置前置处理器 后置处理器,还有他的三级缓存,我们上面只有一级缓存,等等,大牛的设计真的牛皮,骚年们 加油吧!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值