本文共 11413 字,大约阅读时间需要 38 分钟。
自己参考spring,模拟实现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 Mapregistry; /** * 资源加载器 */ 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; }}
这时候,基本的流程就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: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 HashMapmap =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操作,并且也实现类依赖注入。
整个过程的总结如下图:
代码地址: