从山寨Spring中学习Spring IOC原理-XML-Constructor

前言

上一篇【从山寨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自动注入的过程。

猜你喜欢

转载自blog.csdn.net/Smallc0de/article/details/108263448
今日推荐