spring的IOC/DI功能实践
一、写在前面:
做这个Demo主要是为了能够更好的理解Spring的原理,看再多的文章,听再多的讲解最终都最好自己去实现一遍,可以将Spring的功能分块实现,最终自然比较容易将各个功能组合起来。
这个Demo里主要实现的是Spring的IOC功能,即原本需要通过写硬代码来实现初始化复杂类,现在通过配置就能动态的构建复杂类,复杂类的属性值也由配置动态指定,这种实现方式称之为控制反转IOC或叫依赖注入DI;在本例中主要是实现了Spring早期的IOC功能,即通过xml文件配置beans,然后通过XmlBeanFactory来getBean,这里不涉及太多设计模式、异常处理和性能优化,主要还是为了打通过程。对于Spring的注解形式的IOC功能以及AOP功能等还未实践,有时间了也需要归纳知识去实现一遍方能完全理解和记忆。
二、实现功能:
实现了Spring的通过配置文件动态的配置bean,并提供scope、lazy-init、constructor-arg、property的属性或子结点的配置;最终通过factory.getBean("id")来获取从配置文件里构建的bean对象。
三、代码及配置:
1.beans.xml的配置:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="stud1" class="me.silentdoer.simulatespring.pojo.Student"/> <bean id="str1" class="java.lang.String" scope="singleton" lazy-init="true"> <constructor-arg value="UUUUUUUU" type="java.lang.String"/> </bean> <bean id="stud2" class="me.silentdoer.simulatespring.pojo.Student"> <constructor-arg name="uid" value="500" type="java.lang.Long"/> <constructor-arg name="gender" ref="str1" type="java.lang.String"/> <property name="name" value="Hello" type="java.lang.String"/> </bean> </beans>
2.pojo类:Student
package me.silentdoer.simulatespring.pojo; import java.io.Serializable; public class Student implements Serializable { private Long uid; private String name; private String gender; public Student(){ this.name = "silentdoer"; } public Student(Long uid){ this.uid = uid; } public Student(Long uid, String gender){ this.uid = uid; this.gender = gender; } @Override public String toString(){ return String.format("Student-[uid=%s, name=%s, gender=%s]", this.uid, this.name, this.gender); } public Long getUid() { return uid; } public void setUid(Long uid) { this.uid = uid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } }
3.基础类型和其包装类的转换类,如value="500"将500转换为Long型
package me.silentdoer.simulatespring.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @Author Silentdoer * @Since 1.0 * @Version 1.0 * @Date 2018-2-23 16:39 */ public class PrimitiveParser { public static <T> T parse(String type, Object origin){ Logger logger = LoggerFactory.getLogger("myLogger"); if(logger.isDebugEnabled()){ logger.debug(String.format("%s, %s", type, origin)); } Object result = null; switch(type){ case "long": case "java.lang.Long": result = Long.parseLong(origin.toString()); break; // etc. default: throw new UnsupportedOperationException("暂不支持"); } return (T) result; } }
4.配置文件bean的包装类
package me.silentdoer.simulatespring.beans.factory.config; import java.io.Serializable; import java.util.List; /** * 这个不是VO,也不是涉及RPC的POJO,故可以用基础类型以及有默认值 * @Author Silentdoer * @Since 1.0 * @Version 1.0 * @Date 2018-2-19 21:37 */ public class BeanInfo implements Serializable { public static final int SCOPE_PROTOTYPE = 1, SCOPE_SINGLETON = 2; private String id; private String clazz; private Object instance; private int scope = SCOPE_SINGLETON; private boolean lazyInit = false; private List<KeyValueTypePair> constructorArgs; private List<KeyValueTypePair> properties; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public Object getInstance() { return instance; } public void setInstance(Object instance) { this.instance = instance; } public int getScope() { return scope; } public void setScope(int scope) { this.scope = scope; } public boolean isLazyInit() { return lazyInit; } public void setLazyInit(boolean lazyInit) { this.lazyInit = lazyInit; } public List<KeyValueTypePair> getConstructorArgs() { return constructorArgs; } public void setConstructorArgs(List<KeyValueTypePair> constructorArgs) { this.constructorArgs = constructorArgs; } public List<KeyValueTypePair> getProperties() { return properties; } public void setProperties(List<KeyValueTypePair> properties) { this.properties = properties; } public static class KeyValueTypePair { private String key; private Object value; private String type; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public String getType() { return type; } public void setType(String type) { this.type = type; } } }
5.XmlBeanFactory类,用于提供bean
package me.silentdoer.simulatespring.beans.factory; import me.silentdoer.simulatespring.beans.factory.config.BeanInfo; import me.silentdoer.simulatespring.util.PrimitiveParser; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import static me.silentdoer.simulatespring.beans.factory.config.BeanInfo.KeyValueTypePair; /** * @Author Silentdoer * @Since 1.0 * @Version 1.0 * @Date 2018-2-19 21:01 */ public class XmlBeanFactory { private InputStream resource = null; private boolean inited = false; private Map<String, BeanInfo> beansInfo; private Map<String, Class<?>> primitiveAndWrapperTable; public XmlBeanFactory(InputStream inputStream){ this.resource = inputStream; primitiveAndWrapperTable = new HashMap<>(16); primitiveAndWrapperTable.put("long", long.class); primitiveAndWrapperTable.put("java.lang.Long", Long.class); primitiveAndWrapperTable.put("int", int.class); primitiveAndWrapperTable.put("java.lang.Integer", Integer.class); // etc. } protected final void init() throws DocumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { if (inited) { return; } Logger logger = LoggerFactory.getLogger("myLogger"); final InputStream config = this.resource; if (null == config) { throw new IllegalStateException("初始化失败"); } SAXReader reader = new SAXReader(); Document document = reader.read(config); Element root = document.getRootElement(); List<Element> beans = root.elements("bean"); final Map<String, BeanInfo> beanInfoMap = this.beansInfo = new LinkedHashMap<String, BeanInfo>(beans.size()); /** 先构建标签的属性 */ for (Element bean : beans) { Attribute id = bean.attribute("id"); Attribute clazz = bean.attribute("class"); Attribute scope = bean.attribute("scope"); Attribute lazyInit = bean.attribute("lazy-init"); if (id == null || clazz == null) { throw new RuntimeException("配置不合法"); } BeanInfo beanInfo = new BeanInfo(); beanInfo.setId(id.getValue()); beanInfo.setClazz(clazz.getValue()); if (scope != null && scope.getValue().equals("prototype")) { beanInfo.setScope(BeanInfo.SCOPE_PROTOTYPE); } if (lazyInit != null && lazyInit.getValue().equals("true")) { beanInfo.setLazyInit(true); } beanInfoMap.put(id.getValue(), beanInfo); } /** 构建标签的子结点 */ for (Element bean : beans) { List<Element> constructorParams = bean.elements("constructor-arg"); List<Element> properties = bean.elements("property"); String id = bean.attributeValue("id"); BeanInfo beanInfo = beanInfoMap.get(id); List<KeyValueTypePair> conArgs = new ArrayList<KeyValueTypePair>(constructorParams.size()); beanInfo.setConstructorArgs(conArgs); initKeyValueTypePair(conArgs, constructorParams.iterator(), beanInfoMap); List<KeyValueTypePair> pros = new ArrayList<KeyValueTypePair>(properties.size()); beanInfo.setProperties(pros); initKeyValueTypePair(pros, properties.iterator(), beanInfoMap); } /** 根据上面构建出的配置和参数构建bean */ for(BeanInfo bean : beanInfoMap.values()){ //boolean prototype = bean.getScope() == BeanInfo.SCOPE_PROTOTYPE; boolean lazyInit = bean.isLazyInit(); // 只要是非lazyInit则初始化后BeanInfo内的instance一定不为null,这个主要是为了先初始化ref的bean后防止重复初始化该bean if(!lazyInit && bean.getInstance() == null){ Object instance = instantiateBean(bean); bean.setInstance(instance); } } inited = true; } /** * 通过构建好的BeanInfo初始化具体的实例 * @param beanInfo * @return 实例对象 * @throws ClassNotFoundException * @throws NoSuchMethodException */ protected Object instantiateBean(BeanInfo beanInfo) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Logger logger = LoggerFactory.getLogger("myLogger"); Object result = beanInfo.getInstance(); if(result != null) return result; Class<?> instanceClazz = Class.forName(beanInfo.getClazz()); /** ----------------------constructor-arg---------------------- */ List<KeyValueTypePair> constructorArgs = beanInfo.getConstructorArgs(); List<Class> conArgTypes = new ArrayList<Class>(16); List<Object> conArgs = new ArrayList<Object>(16); for(KeyValueTypePair pair : constructorArgs){ //logger.debug(pair.getType()); conArgTypes.add(Class.forName(pair.getType())); Object value = pair.getValue(); // ref的情况则先初始化ref对应的bean if(BeanInfo.class.isInstance(value)){ // 递归优先初始化所有的依赖bean value = instantiateBean((BeanInfo)value); } conArgs.add(value); } /*if(logger.isDebugEnabled()) { logger.debug(conArgTypes.toString()); }*/ Constructor constructor = instanceClazz.getConstructor(conArgTypes.toArray(new Class[conArgTypes.size()])); Object[] initargs = conArgs.toArray(new Object[conArgs.size()]); if(logger.isDebugEnabled()){ for(int i=0;i<initargs.length;i++){ logger.debug("Tag:" + initargs[i].getClass()); } } result = constructor.newInstance(initargs); /** ----------------------property---------------------- */ List<KeyValueTypePair> propertyArgs = beanInfo.getProperties(); for(KeyValueTypePair pair : propertyArgs){ String type = pair.getType(); String name = pair.getKey(); String setter = String.format("set%s%s", name.substring(0, 1).toUpperCase(), name.substring(1)); Method setterM = instanceClazz.getMethod(setter, Class.forName(type)); Object value = pair.getValue(); if(BeanInfo.class.isInstance(value)){ value = instantiateBean((BeanInfo) value); } setterM.invoke(result, value); } return result; } /** * 通过bean的constructor-arg或property配置填充keyValueTypePairs * @param keyValueTypePairs * @param iterator * @param beansContainer */ protected final void initKeyValueTypePair(final List<KeyValueTypePair> keyValueTypePairs, final Iterator<Element> iterator, final Map<String, BeanInfo> beansContainer){ Logger logger = LoggerFactory.getLogger("myLogger"); while(iterator.hasNext()){ Element next = iterator.next(); String name = next.attributeValue("name"); Object value = next.attributeValue("value"); String ref = next.attributeValue("ref"); String type = next.attributeValue("type"); if(value == null && ref == null || value != null && ref != null){ throw new RuntimeException("配置不合法"); } KeyValueTypePair e = new KeyValueTypePair(); e.setKey(name); e.setType(type); if(value != null){ // 需要转换 if(primitiveAndWrapperTable.get(type) != null){ value = PrimitiveParser.parse(type, value); } e.setValue(value); }else{ // ref // NOTE 目前只是初始化BeanInfo,还没到初始化具体的Bean对象 BeanInfo refBean = beansContainer.get(ref); // name=gender ref=str1 // 暂且规定ref的bean要先配置 if(refBean == null){ // 也可以改成从配置里查找name,有则先初始化该BeanInfo,然后赋值 throw new RuntimeException("配置不合法"); } e.setValue(refBean); } keyValueTypePairs.add(e); } } public <T> T getBean(String id){ try { init(); }catch (Throwable ex){ throw new IllegalStateException(ex); } Object result = null; final Map<String, BeanInfo> beans = this.beansInfo; BeanInfo beanInfo = beans.get(id); result = beanInfo.getInstance(); if(result == null){ try { result = instantiateBean(beanInfo); }catch (Exception ex){ ex.printStackTrace(); } } if(beanInfo.getScope() == BeanInfo.SCOPE_PROTOTYPE){ try { Method clone = Object.class.getMethod("clone"); clone.setAccessible(true); result = clone.invoke(beanInfo.getInstance()); }catch (Exception ex){ ex.printStackTrace(); } } return (T) result; } }
6.main方法类
import me.silentdoer.simulatespring.beans.factory.XmlBeanFactory; import me.silentdoer.simulatespring.pojo.Student; import java.io.InputStream; /** * @Author Silentdoer * @Since 1.0 * @Version 1.0 * @Date 2018-2-19 20:01 */ public class Entrance { public static void main(String[] args){ InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream("beans.xml"); System.out.println(resource == null); XmlBeanFactory factory = new XmlBeanFactory(resource); String str1 = factory.getBean("str1"); System.out.println(str1); Student student = factory.getBean("stud1"); System.out.println(student); Student student2 = factory.getBean("stud2"); System.out.println(student2); } }
最终main方法输出为:
UUUUUUUU Student-[uid=null, name=silentdoer, gender=null] Student-[uid=500, name=Hello, gender=UUUUUUUU]
Spring的基础的IOC功能实现完毕,源码放在我的GitHub上:https://github.com/Silentdoer/demos/tree/master/模拟Spring的IOC功能/Demo.SimulateSpringIOC
posted on 2018-02-21 11:50 Silentdoer 阅读(190) 评论(0) 编辑 收藏 举报