欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring-IOC学习(自己实现IOC功能)

程序员文章站 2022-07-12 12:56:46
...

自己参考spring,模拟实现Spring的IOC功能,加深理解.

项目结构如下:

Spring-IOC学习(自己实现IOC功能)

   下图是个人对IOC理解画的关系图

Spring-IOC学习(自己实现IOC功能)

 

接口:

BeanFactory ,IOC的容器,需要有注册Bean和获取Bean的方法

package com.szt.myioc.factory;

import  com.szt.myioc.BeanDefinition;

/**
 * @author: sunzhitao
 * @date: 2018/7/22 16:26
 * @description: IOC容器的接口 需要两个基本方法 注册bean 获取bean
 */
public interface BeanFactory {

    /**
     * 根据bean的名字获取ioc容器中的bean
     * @return bean实例
     * @throws Exception
     */
    Object getBean(String name) throws Exception;

    /**
     * 将bean注册到ioc容器中
     * @param name
     * @param beanDefinition
     * @throws Exception
     */
    void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws  Exception;
}

  IOC容器的初始化,分为三步  BeanDefinition的Resource定位、载入以及注册。Spring ioc容器有了,需要BeanDefinition.BeanDefinition需要一些方法,需要一个 Bean 对象,一个Class对象,一个ClassName字符串,还需要一个元素集合 PropertyValues。这些组成一个最基本的 BeanDefinition 类。

/**
 * @author: sunzhitao
 * @date: 2018/7/22 16:28
 * @description: BeanDefinition bean定义
 */
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;
  }

}

有了BeanDefinition 那么 容器初始化过程的中,Resource的定位,载入注册,需要一个从XML中读取并解析为 BeanDefinition 的操作类,定义一个 BeanDefinitionReader 接口,然后实现这个接口的类用来完成Resource的定位操作.还需要一个资源加载器ResourceLoader,用于加载xml文件.

/**
 * @author: sunzhitao
 * @date: 2018/7/22 16:36
 * @description: 抽象的bean定义读取类
 */
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

  /**
   * 注册bean容器
   */
  private Map<String, BeanDefinition> registry;

  /**
   * 资源加载器
   */
  private ResourceLoader resourceLoader;

  /**
   * 构造器器必须有一个资源加载器, 默认插件创建一个map容器
   *
   * @param resourceLoader 资源加载器
   */
  protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
    this.registry = new HashMap<>();
    this.resourceLoader = resourceLoader;
  }

  /**
   * 获取容器
   */
  public Map<String, BeanDefinition> getRegistry() {
    return registry;
  }

  /**
   * 获取资源加载器
   */
  public ResourceLoader getResourceLoader() {
    return resourceLoader;
  }

}

这时候,基本的流程就ok了.BeanDefinitionReader 从xml中读取bean的配置信息,生成BeanDefinition,放到BeanFactory容器中,初始化之后,使用getBean()方法获取bean.

实现:

1.读取xml文件中对bean的定义信息,解析成BeanDefinition

从xml中读取bean信息过程中,需要用到类资源加载器 ResourceLoader.类资源加载器,需要根据给定的资源路径加载资源,所以需要一个ResourceUrl.

public interface Resource {

  /**
   * 获取输入流
   */
  InputStream getInputstream() throws Exception;
}
public class ResourceLoader {

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

ResourceLoader需要用到ResourceUrl.

/**
 * 资源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();

  }

}

以上所做全都是为了满足AbstractBeanDefinitionReader 的需要,现在需要一个类继承AbstractBeanDefinitionReader,去实现具体的功能.现在定义一个XmlBeanDefinitionReader 继承他,然后读取xml,获取bean信息,然后再将获取到的转化成一个对象,注册到我们的容器中.

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    /**
     * 构造器器必须有一个资源加载器, 默认插件创建一个map容器
     *
     * @param resourceLoader 资源加载器
     */
    protected XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }

    /**
     * 解析xml的方法
     * @param Location
     * @throws Exception
     */
    public void readerXML(String Location) throws Exception{
        //创建一个资源加载器
        ResourceLoader resourceLoader = new ResourceLoader();
        //从资源加载器获取流
        InputStream inputstream = resourceLoader.getResource(Location).getInputstream();
        //获取文档建造者工厂实例
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //工厂创建文档建造者
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        //文档建造者解析流,返回文档对象
        Document doc = documentBuilder.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 < nl.getLength(); 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 < propertyNode.getLength(); 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: <property> element for property '"
                                        + name + "' must specify a ref or value");
                    }
                    // 如果不为空,测创建一个 “bean的引用” 实例,构造参数为名称,实例暂时为空
                    BeanReference beanRef = new BeanReference(name);
                    // 向给定的 “bean定义” 中添加成员变量
                    beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanRef));
                }
            }
        }
    }
}

上面成功解析xml,并且将bean放入到了容器中,但是注意的是,这时候放入的是BeanDefinitionReader 的 Map 中。还没有给ioc容器.

已经有了BeanFactory,但是没有实现,现在根据BeanFactory定义一个抽象类AbstractBeanFactory.

/**
 * @author: sunzhitao
 * @date: 2018/7/22 18:14
 * @description: beanfactory的实现类  实现了 bean 的方法,
 * 包含一个map,用于存储bean 的名字和bean的定义
 */
public abstract class AbstractBeanFactory implements BeanFactory {

    /**
     * 容器
     */
    private HashMap<String, BeanDefinition> map =new HashMap<>();

    /**
     * 根据bean的名称获取bean,没有则抛异常,有,从bean定义对象获取bean实例
     * @return bean的实例对象
     * @throws Exception
     */
    @Override
    public Object getBean(String name) throws Exception {
        BeanDefinition beanDefinition = map.get(name);
        if(beanDefinition == null){
            throw new IllegalArgumentException("No bean Name "+name+" is defined");
        }

        Object bean = beanDefinition.getBean();
        if(bean==null){
            bean = doCreate(beanDefinition);
        }
        return bean;
    }

    /**
     * 注册 bean定义的抽象方法实现
     * @param name
     * @param beanDefinition
     * @throws Exception
     */
    @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;
}

上面的抽象类,里面用一个Map用来作为管理bean的容器,给到一个获取bean的getBean()方法,一个bean的注册方法.现在新建一个类实现这个抽象方法,完成最终的bean注册.

/**
 * @author: sunzhitao
 * @date: 2018/7/22 20:08
 * @description: 实现自动注入和递归注入(spring的标准实现类 DefaultListableBeanFactory)
 */
public class AutowireBeanFactory extends AbstractBeanFactory {

    /**
     * 根据bean定义创建实例,并且将实例作为key,bean定义作为value存放,并且调用addPropertyValue方法
     * 为给定的bean的属性进行注入
     * @param beandefinition bean定义
     * @return
     * @throws Exception
     */
    @Override
    Object doCreate(BeanDefinition beandefinition) throws Exception {
        Object bean = beandefinition.getBeanclass().newInstance();
        addPropertyValue(bean,beandefinition);
        return null;
    }

    /**
     * 给定一个bean定义和一个bean实例,为给定的bean中的属性注入实例。
     */
    protected void addPropertyValue(Object bean, BeanDefinition beandefinition) throws Exception {
        // 循环给定 bean 的属性集合
        for (PropertyValue pv : beandefinition.getPropertyValues().getPropertyValues()) {
            // 根据给定属性名称获取 给定的bean中的属性对象
            Field declaredField = bean.getClass().getDeclaredField(pv.getname());
            // 设置属性的访问权限
            declaredField.setAccessible(true);
            // 获取定义的属性中的对象
            Object value = pv.getvalue();
            // 判断这个对象是否是 BeanReference 对象
            if (value instanceof BeanReference) {
                // 将属性对象转为 BeanReference 对象
                BeanReference beanReference = (BeanReference) value;
                // 调用父类的 AbstractBeanFactory 的 getBean 方法,根据bean引用的名称获取实例,此处即是递归
                value = getBean(beanReference.getName());
            }
            // 反射注入bean的属性
            declaredField.set(bean, value);
        }

    }
}

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

整个过程的总结如下图:

Spring-IOC学习(自己实现IOC功能)

代码地址:https://github.com/sunzhitao/springboot/tree/master/myioc

相关标签: spring-ioc