接上回《spring概述及环境搭建》
上回讲到只需要在xml中配置一个 <bean><property name="msg" value="喜欢你,没道理! "></property></bean>配置一个子标签,便可以对Helloworld中msg行赋值,是否觉蛋疼?刚学习spring时,小飞我也觉得挺纳闷的,单单配置个属性就能进行赋值,这不是坑吗!其实,当熟悉原理之后,你会觉得,靠,原来也就那么一会事!现在小飞通过代码模拟一下spring实现Bean的原理!
上回的xml配置文件
<!-- 部分1 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 部分2 -->
<!-- 配置组件,上面编写的helloWold类 -->
<bean id="sayBean" class="com.langfei.helloworld.HelloWorld">
<!-- 依赖注入 -->
<property name="msg" value="喜欢你,没道理! "></property>
</bean>
</beans>
实体类
public class HelloWorld {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void say()
{
System.out.println(msg);
}
}
测试类
public class SayMsg {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld hw = (HelloWorld) app.getBean("sayBean");
hw.say();
}
}
#########################【山寨版spring 依赖注入及bean实例化】#######################################
第一步:导包
dom4j-1.6.1.jar xml解析使用包
jaxen-1.1-beta-6.jar dom4j依赖包
第二步:模拟BeanDefinition类
/**
* 模拟spring中的BeanDefinition
* 此处该类是一个简单的bean类,用于模拟id和classname对应关系
* @author 狼飞
*/
public class MyBeanDefinition {
private String id; //对应xml<bean>标签id属性值
private String classname; //对应class 属性值
public MyBeanDefinition(String id, String classname){
this.id = id;
this.classname = classname;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
}
第三步:模拟ClassPathXmlApplicationContext
package com.langfei.helloworld;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
/**
* 本类模拟spring ClassPathXmlApplicationContext类,对xml文件的进行解析,获取其中包含的实体类配置信息,
* 通过反射,实现山寨版的spring依赖注入及bean实例化
* @author 狼飞
*
*/
public class MyClassPathXmlApplicationContext {
private List<MyBeanDefinition> beanDefinitions = new ArrayList<MyBeanDefinition>();
// 用于保存解析xml中的bean实例,key为对应id value为class
private Map<String, Object> sigletons = new HashMap<String, Object>();
public MyClassPathXmlApplicationContext(String xmlName) {
this.readXml(xmlName); // 解析xml
}
private void readXml(String xmlName) {
SAXReader saxReader = new SAXReader();
Document document = null;
URL xmlPath = this.getClass().getClassLoader().getSystemResource(xmlName);
try {
document = saxReader.read(xmlPath);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Map<String, String> nsMap = new HashMap<String, String>(); //用于缓存命名空间map集
nsMap.put("ns", "http://www.springframework.org/schema/beans");// 命名空间
XPath xsub = document.createXPath("//ns:beans/ns:bean"); // 创建beans/bean查询路径
xsub.setNamespaceURIs(nsMap); //设置命名空间uri
List<Element> beansElements = xsub.selectNodes(document); //获取对应空间下节点集
for (Element element : beansElements) {
String id = element.attributeValue("id");
String clazz = element.attributeValue("class"); //<bean>标签的id属性值及class属性值
MyBeanDefinition beanDefinition = new MyBeanDefinition(id, clazz);
beanDefinitions.add(beanDefinition);
List<Object> propNames = new ArrayList<Object>(); //bean子标签<property>的name属性对应的值,也即实体对象如HellowWorld的属性名
List<Object> propValues = new ArrayList<Object>();//bean子标签<property>的value属性对应的值,也即实体对象如HellowWorld的属性值
//采用迭代方法获取<bean>标签中子标签
for (Iterator<Element> iterator = element.elementIterator(); iterator
.hasNext();) {
Element elementSon = (Element) iterator.next();
for (Iterator ia = elementSon.attributeIterator(); ia.hasNext();) {
ia.next();//获取子标签的name对应的属性值及value属性对应的值
propNames.add(elementSon.attributeValue("name"));
propValues.add(elementSon.attributeValue("value"));
}
}
try {
//将<bean>标签class属性值转换为Class实例
Class clazzClass = Class.forName(clazz);
Object object = clazzClass.newInstance();//通过反射,将class转换为实体对象实例
Field[] fields = clazzClass.getDeclaredFields(); //获取实体对象所有属性
for (Field f : fields) {
for (int i = 0; i < propNames.size(); i++) {
String propername = f.getName();
if (propNames.get(i).equals(propername)) {
StringBuilder sb = new StringBuilder();
String methodName = sb //根据属性名拼接setXxx方法
.append("set")
.append(propername.substring(0, 1)
.toUpperCase())
.append(propername.substring(1)).toString();
Method method = clazzClass.getMethod(methodName,
String.class);
method.invoke(object, propValues.get(i)); //执行set方法,为属性赋值,模拟spring的依赖注入
}
}
}
sigletons.put(id, object);
} catch (Exception e) {
e.printStackTrace();
}
propNames.clear();
propValues.clear();
}
}
public Object getBean(String beanName) {
return this.sigletons.get(beanName);
}
}
第四步:测试
实体类不需要任何改变,将测试类修改如下
public class SayMsg {
public static void main(String[] args) {
/*ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld hw = (HelloWorld) app.getBean("sayBean");
hw.say();*/
MyClassPathXmlApplicationContext applicationContext = new MyClassPathXmlApplicationContext("beans.xml");
HelloWorld hWorld = (HelloWorld) applicationContext.getBean("sayBean");
hWorld.say();
}
}
至此,spring bean原理模拟结束,相比正在的spring bean依赖注入实现,这可谓是小巫见大巫,不是同个级别,不过原理都是那么一回事!
原文地址:http://blog.163.com/langfei520@yeah/blog/static/172710222201282472944140/