前言
上一篇【从山寨Spring中学习Spring IOC原理-XML-Setter】我们完成了对Spring中Setter方法的山寨,那么这次就要改造一下我们的程序,让其满足使用构造方法注入。进入之前希望有条件的读者,下手把笔者贴出来的代码敲一遍,或者复制一遍在编译器里跟踪运行一下。这样每一步取了什么值,为什么这样写,就会更加明白了。更多Spring内容进入【Spring解读系列目录】。
修改程序
如果是使用构造方法进行构建,那么就需要在UserServiceImpl使用构造方法传递UserDao进去,修改代码为:
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public void find() {
System.out.println("UserServiceImpl find()");
userDao.query();
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl(UserDao userDao) {
//创建构造方法
this.userDao = userDao;
}
}
同时我们的配置文件也要修改,为了简化代码,只让有一个构造方法。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="dao" class="com.demo.dao.UserDaoImpl"></bean>
<bean id="service" class="com.demo.service.UserServiceImpl">
<!--假设只有一个构造方法-->
<constructor-arg name="userDao" ref="dao"></constructor-arg>
</bean>
</beans>
最终修改的大头就落在了BeanFactory上,为了更加清楚,我们重构一个类BeanFactoryCon:
public class BeanFactoryCon {
Map<String, Object> map=new HashMap<>();
public BeanFactoryCon(String xml) {
parseXml(xml);
}
public void parseXml(String xml){
String path=this.getClass().getResource("/").getPath()+xml;
File file=new File(path);
SAXReader reader = new SAXReader();
try {
Document document = reader.read(file);
Element elementRoot=document.getRootElement();
//拿取xml中元素的内容
for (Iterator<Element> itFirst = elementRoot.elementIterator(); itFirst.hasNext();) {
Element elementFirstChild = itFirst.next();
//取到id值
Attribute attribute=elementFirstChild.attribute("id");
String beanName=attribute.getValue();
//取到类全名
Attribute attribute2=elementFirstChild.attribute("class");
String clazzName=attribute2.getValue();
//获取标签中的类全名
Class clazz=Class.forName(clazzName);
//当我们写了构造方法的时候,默认的构造方法就不存在了,所以不能直接newInstance()构建实例,而应该使用标签里的构造标签
Object object=null;
//维护依赖关系,找到依赖关系:判断是否有属性,然后判断属性是否有对应的constructor-arg
// 如果有则注入,所以每循环到一个bean就要拿出子标签
for (Iterator<Element> itSecond = elementFirstChild.elementIterator(); itSecond.hasNext();) {
Element elementSecondChild =itSecond.next();
if (elementSecondChild.getName().equals("constructor-arg")){
//把map中存的UserDao对象拿出来
String refValue=elementSecondChild.attribute("ref").getValue();
Object injectObj=map.get(refValue);
//把UserDao对象构造为一个类
Class injectObjClazz=injectObj.getClass();
String nameValue=elementSecondChild.attribute("name").getValue();
//拿到属性类型,为了创建构造方法
Field field=clazz.getDeclaredField(nameValue);
//根据属性类型创建构造方法对象,这里也可以通过实现的接口去构造
Constructor constructor=clazz.getConstructor(field.getType());
//clazz.getConstructor(injectObjClazz.getInterfaces()[0]);
//使用构造方法对象把UserDao对象注入进去。
object=constructor.newInstance(injectObj);
}
}
if(object==null){
//没有子标签,意味着没有依赖所以new出来
object=clazz.newInstance();
}
map.put(beanName,object);
}
System.out.println(map.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getBean(String bean){
return map.get(bean);
}
}
修改完毕以后,修改Test类,运行测试:
public class Test {
public static void main(String[] args) {
BeanFactoryCon beanFactoryCon=new BeanFactoryCon("spring.xml");
UserService service= (UserService) beanFactoryCon.getBean("service");
service.find();
}
}
运行结果:
UserServiceImpl find()
UserDaoImpl query 1
总结
在对上个程序进行简单的修改以后,我们就完成了容器基于构造方法的注入,其实通过这些例子的构造,大体也能猜出Spring源码到底的怎么写的,只是那些大神们的思维更加的严谨,更加的详实。目前已经注入没有问题了,既然要山寨Spring,就不能少了Spring的自动注入。所以下一篇【从山寨Spring中学习Spring IOC原理-byType自动装配】我们就会模拟一个Spring自动注入的过程。