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

上一篇:Math.Round函数详解


下一篇:Java中Math.round()函数