一、写在前面:

  做这个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

spring的IOC/DI功能实践的更多相关文章

  1. Spring框架——IOC&DI

    Spring Spring 目标 内容 Spring与web整合的原理 Spring 中包含的关键特性 Spring架构图 企业级框架 企业级系统 IOCDI IOC DI IOC和DI 为什么使用依 ...

  2. Spring框架-IOC/DI详细学习

    一.IOC/DI概念 参考博客:https://www.cnblogs.com/xdp-gacl/p/4249939.html IOC(inversion of control, 控制反转)是一种设计 ...

  3. Spring之IOC/DI(反转控制/依赖注入)_入门Demo

    在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new ob ...

  4. Spring的IOC/DI使用到的技术

    一.了解Spring IOC/DI 1:Spring有两大核心技术,控制反转(Inversion of Control, IOC)/依赖注入(Dependency Injection,DI)和面向切面 ...

  5. 个人对spring的IOC+DI的封装

    暂时支持8种基本数据类型,String类型,引用类型,List的注入. 核心代码 package day01; import java.lang.reflect.Field;import java.l ...

  6. Spring基础[IOC/DI、AOP]

    一.Spring作用:管理项目中各种业务Bean(service类.Dao类.Action类),实例化类,属性赋值 二.Spring IOC(Inversion of Control )控制反转,也被 ...

  7. Spring理解IOC,DI,AOP作用,概念,理解。

    IOC控制反转:创建实例对象的控制权从代码转换到Spring容器.实际就是在xml中配置.配置对象 实例化对象时,进行强转为自定义类型.默认返回类型是Object强类型. ApplicationCon ...

  8. Spring注解IOC/DI(4)

    2019-03-08/11:10:17 演示:使用注解的方式完成注入对象中的效果 注解参考链接:https://www.cnblogs.com/szlbm/p/5512931.html Spring中 ...

  9. 解释Spring中IOC, DI, AOP

    oc就是控制翻转或是依赖注入.通俗的讲就是如果在什么地方需要一个对象,你自己不用去通过new 生成你需要的对象,而是通过spring的bean工厂为你长生这样一个对象.aop就是面向切面的编程.比如说 ...

随机推荐

  1. 基于OpenGL编写一个简易的2D渲染框架-12 重构渲染器-BlockAllocator

    BlockAllocator 的内存管理情况可以用下图表示 整体思路是,先分配一大块内存 Chunk,然后将 Chunk 分割成小块 Block.由于 Block 是链表的一个结点,所以可以通过链表的 ...

  2. display:inline、block、inline-block区别

    display:inline.block.inline-block区别 display:block就是将元素显示为块级元素. display:inline就是将元素显示为行内元素. inline-bl ...

  3. 转载:mysql binlog同步redis

    ref: https://wenku.baidu.com/view/5d9d04ac6394dd88d0d233d4b14e852458fb39c4.html

  4. tar 压缩文件指定目录

    tar -cjf /app/tmp/app/test.tar.bz2 -C /app/tmp  res_test.csv 将/app/tmp 目录下 res_test.csv文件压缩到/app/tmp ...

  5. RISC处理器

     RISC(精简指令集算法)处理器是经过硬件的精简只执行很有限的最常用的那部分指令的处理器.因为通过研究发现,只有 大约 20%的指令是最常用的,把处理器能执行的指令数目减少到 最低限度,对它们的执行 ...

  6. denyhosts配置详解

    DenyHosts官方网站为:http://denyhosts.sourceforge.net 用DenyHosts可以阻止试图猜测SSH登录口令,它会分析/var/log/secure等日志文件,当 ...

  7. luoguP3366 [模板] 最小生成树

    题目链接:https://www.luogu.org/problemnew/show/P3366 思路: 求最小生成树的模板题,求MST有两种算法——Prim.Kruskal. 两者区别:Prim在稠 ...

  8. 第八章 高级搜索树 (xa4)红黑树:删除

  9. Aactivity和Service之间的通信

    一.在activity中定义三个按钮 一个开启服务  一个关闭服务,还有一个是向服务发送广播 当创建出Serevice时先执行Service的onCreate()创建服务后只执行一次 以后每次点击开启 ...

  10. spring开发Eclipse需要做设置

    1. 统一工作空间的编码,选择UTF-8 2. 把创建JSP页面的编码修改UTF-8 3. 重新配置Tomcat服务器 * 先配置Tomcat服务器 * 选择服务器 --> open --> ...