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
spring的IOC/DI功能实践的更多相关文章
- Spring框架——IOC&DI
Spring Spring 目标 内容 Spring与web整合的原理 Spring 中包含的关键特性 Spring架构图 企业级框架 企业级系统 IOCDI IOC DI IOC和DI 为什么使用依 ...
- Spring框架-IOC/DI详细学习
一.IOC/DI概念 参考博客:https://www.cnblogs.com/xdp-gacl/p/4249939.html IOC(inversion of control, 控制反转)是一种设计 ...
- Spring之IOC/DI(反转控制/依赖注入)_入门Demo
在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new ob ...
- Spring的IOC/DI使用到的技术
一.了解Spring IOC/DI 1:Spring有两大核心技术,控制反转(Inversion of Control, IOC)/依赖注入(Dependency Injection,DI)和面向切面 ...
- 个人对spring的IOC+DI的封装
暂时支持8种基本数据类型,String类型,引用类型,List的注入. 核心代码 package day01; import java.lang.reflect.Field;import java.l ...
- Spring基础[IOC/DI、AOP]
一.Spring作用:管理项目中各种业务Bean(service类.Dao类.Action类),实例化类,属性赋值 二.Spring IOC(Inversion of Control )控制反转,也被 ...
- Spring理解IOC,DI,AOP作用,概念,理解。
IOC控制反转:创建实例对象的控制权从代码转换到Spring容器.实际就是在xml中配置.配置对象 实例化对象时,进行强转为自定义类型.默认返回类型是Object强类型. ApplicationCon ...
- Spring注解IOC/DI(4)
2019-03-08/11:10:17 演示:使用注解的方式完成注入对象中的效果 注解参考链接:https://www.cnblogs.com/szlbm/p/5512931.html Spring中 ...
- 解释Spring中IOC, DI, AOP
oc就是控制翻转或是依赖注入.通俗的讲就是如果在什么地方需要一个对象,你自己不用去通过new 生成你需要的对象,而是通过spring的bean工厂为你长生这样一个对象.aop就是面向切面的编程.比如说 ...
随机推荐
- Effective C++笔记——day01
1.当我们看到赋值符号时,请小心,因为"="也可以用来调用copy构造函数 Widget w3 = w2; //调用copy构造函数,而不是copy赋值操作符 2.不明确的行为: ...
- centos6.8 安装python2.7 or python3.6
from:https://danieleriksson.net/2017/02/08/how-to-install-latest-python-on-centos/ 准备 # Start by mak ...
- C++ 0x 使用 shared_ptr 自动释放, 防止内存泄漏
最近在研究 cocos2d-x 3.0 ,它在创建类的对象时比如 Layer 时, 并不是直接使用 new , 而是使用一个宏方法 CREATE_FUNC(MyLayer);. 这个宏就是自动的创建 ...
- ndarray
ndarray ndarray-多维数组对象 ndarray数组分两部分 实际数据 描述数据的元数据(数据类型/维度等) ndarray所有元素类型相同,下标从0开始 import numpy as ...
- Springboot IDEA eclipse 打包
在开发springboot项目中,我们会进行打包发布项目,springboot推荐以jar包方式发布,相对之下 jar运行的效率比起war项目快很多. 打包切记: 1.查看项目里面有没有其他的main ...
- hdoj Max Sum Plus Plus(DP)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1024 题意:----最大M子段和问题给定由 n个整数(可能为负整数)组成的序列a1,a2,a3,……, ...
- Codeforces Round #534 (Div. 2)
B. Game with string 题意: 给出一个字符串s只包括小写字母.当轮到一个玩家的时候,他可以选择两个连续且相等的字母并且删除它.当一个玩家没得删的时候他就输了. 题解: 乍一看有点懵, ...
- 134. Gas Station (Array; DP)
There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. You ...
- php页面的基本语法
概述: 1. PHP 脚本在服务器上执行,然后将纯 HTML 结果发送回浏览器. 2. PHP 脚本以 <?php 开始,以 ?> 结束,可以放到文档中的任何位置. 3. 当 PHP 解析 ...
- 关于weblogic报UnsatisfiedLinkError Native Library xxx.so already loaded
一.场景 最近写的一个系统,在Tomcat测试完后说要改使用weblogic,于是在服务器上安装了weblogic,捣鼓了半天,一个个问题冒了出来,其中就有个比较麻烦的报错:UnsatisfiedLi ...