把代理和反射系统学完后,突然想自己实现一下spring是如何加载xml配置文件的,当然这里要用到反射。Spring 通过 XML 配置模式装载 Bean 的过程:
- 将程序内所有 XML 或 Properties 配置文件加载入内存中
- Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息
- 使用反射机制,根据这个字符串获得某个类的Class实例
- 动态配置实例的属性
这样做的好处是:
- 不用每一次都要在代码里面去new或者做其他的事情
- 以后要改的话直接改配置文件,代码维护起来就很方便了
- 有时为了适应某些需求,Java类里面不一定能直接调用另外的方法,可以通过反射机制来实现
那我们开始吧
1.准备工作
1) Maven+Idea
2) Dom4j解析xml
2.创建Person实体
package com.cxx.reflect;
/**
* @Author: cxx
* @Date: 2018/6/1 10:23
*/
public class Person {
private String name;
public int age;
private int score;
public Person(){ }
public Person(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
3.在resources下建立spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="people" class="com.cxx.reflect.Person">
<property name="name" value="cxx"/>
<property name="age" value="20"/>
<property name="score" value="88"/>
</bean>
</beans>
4.编写SpringBeanFactory
package com.cxx.reflect;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @Author: cxx
* 模拟spring 加载xml配置文件(dom4j)
* @Date: 2018/6/1 16:38
*/
public class SpringBeanFactory {
//初始化的bean全用map集合保存
private static Map<String,Object> beanMap = new HashMap<>();
public static void main(String[] args) {
init("spring.xml");
Person person = (Person) beanMap.get("people");
System.out.println(person.toString());
}
/**
* bean工厂初始化
* @param xml
*/
public static void init(String xml){
try {
SAXReader reader = new SAXReader();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//从class目录下获取指定的xml
InputStream is = classLoader.getResourceAsStream(xml);
Document doc = reader.read(is);
Element rootElement = doc.getRootElement();
//遍历bean
Element foo;
for (Iterator i =rootElement.elementIterator("bean");i.hasNext();){
foo = (Element) i.next();
//获取id和class
Attribute id = foo.attribute("id");
Attribute aClass = foo.attribute("class");
//利用反射机制获取Class对象
Class bean = Class.forName(aClass.getText());
//获取class信息
BeanInfo info = Introspector.getBeanInfo(bean);
//获取其属性描述
PropertyDescriptor pd[] = info.getPropertyDescriptors();
//设置方法
Method mSet = null;
//创建一个对象
Object obj = bean.newInstance();
//遍历该bean的property属性
for (Iterator ite = foo.elementIterator("property");ite.hasNext();){
Element foo2 = (Element) ite.next();
//获取name的属性
Attribute name = foo2.attribute("name");
//获取value值
Attribute value = foo2.attribute("value");
for (PropertyDescriptor pp : pd) {
if (pp.getName().equalsIgnoreCase(name.getText())){
mSet = pp.getWriteMethod();
Class<?>[] types = mSet.getParameterTypes();
for (Class<?> type : types) {
if (type.getName().equals("int")){
//转换为整型
mSet.invoke(obj,Integer.parseInt(value.getText()));
}else {
//利用java反射调用set方法
mSet.invoke(obj,value.getText());
}
}
}
}
}
beanMap.put(id.getText(),obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.结果
总结
还要很多地方可以继续完善,行内有一句这样的老话:反射机制是Java框架的基石。一般应用层面很少用,不过这种东西,现在很多开源框架基本都已经封装好了,自己基本用不着写,不过了解一下还是不错的,其实也很容易理解。