作這個Demo主要是爲了可以更好的理解Spring的原理,看再多的文章,聽再多的講解最終都最好本身去實現一遍,能夠將Spring的功能分塊實現,最終天然比較容易將各個功能組合起來。java
這個Demo裏主要實現的是Spring的IOC功能,即本來須要經過寫硬代碼來實現初始化複雜類,如今經過配置就能動態的構建複雜類,複雜類的屬性值也由配置動態指定,這種實現方式稱之爲控制反轉IOC或叫依賴注入DI;在本例中主要是實現了Spring早期的IOC功能,即經過xml文件配置beans,而後經過XmlBeanFactory來getBean,這裏不涉及太多設計模式、異常處理和性能優化,主要仍是爲了打經過程。對於Spring的註解形式的IOC功能以及AOP功能等還未實踐,有時間了也須要概括知識去實現一遍方能徹底理解和記憶。git
實現了Spring的經過配置文件動態的配置bean,並提供scope、lazy-init、constructor-arg、property的屬性或子結點的配置;最終經過factory.getBean("id")來獲取從配置文件裏構建的bean對象。github
<?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>
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; } }
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; } }
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; } } }
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; } }
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); } }
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.SimulateSpringIOCspring