这篇博文主要模拟spring的ClassPathXmlApplicationContext的getBean(id)方法,以及实体类值得注入。
思路大概是解析 bean.xml文件,获取 bean的ID及class。通过解析到的类名就可以反射获取到该类的方法及实例 对象,最后注值。
1.先看下项目整体结构
2.准备好两个实体类。StuManager里面有一个Student的引用,这是为了模拟后面的ref注值。
3.创建ApplicationContext接口
package org.springframework.context;
public interface ApplicationContext {
public Object getBean(String beanId);
}
4.创建ClassPathXmlApplicationContext类
package org.springframework.context;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class ClassPathXmlApplicationContext implements ApplicationContext{
//这个map集合用来代表IOC容器
Map<String , Object> map = new HashMap<String, Object>();
public ClassPathXmlApplicationContext(String xmlFile) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//解析beans文件
InputStream is = this.getClass().getClassLoader().getResourceAsStream(xmlFile);
SAXReader reader = new SAXReader();
Document doc = reader.read(is);
Element root = doc.getRootElement();
//获取所有bean信息
List<Element> beans = root.elements();
//遍历
for (Element bean : beans) {
String id=bean.attributeValue("id");
String className = bean.attributeValue("class");
//根据读取的类名创建字节码对象
//反射创建对象的三种方式:1.通过类名创建.。2.通过类对象创建 3.通过Class.forName(全类名)
Class cls=Class.forName(className);
//通过字节码对象去创建实例
Object obj = cls.newInstance();
//把创建好的对象放进容器里
map.put(id, obj);
//获取所有方法
Method[] methods = cls.getMethods();
//获取要注入的属性值
List<Element> pros = bean.elements();
//遍历方法
for (Method method : methods) {
//获取set方法
String methodName = method.getName();
if (methodName.startsWith("set")) { //如果是set开头的方法名,说明此方法是set方法
//遍历属性值
for (Element pro:pros) {
//获取属性名
String proName = pro.attributeValue("name");
//判断方法和属性是否匹配
if (methodName.substring(3).equalsIgnoreCase(proName)) {
//获取属性值
String proValue = pro.attributeValue("value");
//传入参数到方法后执行
if (proValue!=null) {
method.invoke(obj, proValue);
}else { //说明它是一个ref属性
method.invoke(obj, map.get(pro.attributeValue("ref")));
}
}
}
}
}
}
}
@Override
public Object getBean(String beanId) {
return map.get(beanId);
}
}
5.写一个bean.xml。放在src目录下。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="stu" class="bean.Student">
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
</bean>
<bean id="stum" class="bean.StuManager">
<property name="student" ref="stu"></property>
</bean>
</beans>
6.测试运行
public class _main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, DocumentException, IllegalArgumentException, InvocationTargetException {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
Student stu = (Student)ac.getBean("stu");
StuManager stm = (StuManager) ac.getBean("stum");
System.out.println(stu);
System.out.println(stm);
}
}