简单模拟spring装载注入bean原理

来源:互联网 发布:ios sql 编辑:程序博客网 时间:2024/05/22 15:59

简单模拟spring装载注入bean原理

前言:自己工作时间也将近9个月了,但还没系统学习过spring的知识,近来跟着马士兵老师的spring2.5的视频学,一步一个脚印的向前走。

主要步骤:

一 将需要被加载的类放入beans.xml中
二 通过jdom读取xml配置
三 通过反射机制,将beans.xml中对应类加载进来
beans.xml文件如下:
<beans>    <bean id="userDAO" class="org.zeng.dao.impl.UserDAOImpl" />    <bean id="userService" class="org.zeng.service.UserService">        <property name="userDAO" bean="userDAO"/>    </bean></beans>
UserService类代码如下:
package org.zeng.service;import org.zeng.dao.UserDAO;import org.zeng.dao.impl.UserDAOImpl;import org.zeng.model.User;public class UserService {    private UserDAO userDAO;    public UserDAO getUserDAO() {        return userDAO;    }    public void setUserDAO(UserDAO userDAO) {        this.userDAO = userDAO;    }    public void add(User u) {        this.userDAO.save(u);    }}
#读取xml文件,及利用反射机制,加载类的代码:
package org.zeng.spring;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.HashMap;import java.util.List;import java.util.Map;import org.jdom.Document;import org.jdom.Element;import org.jdom.JDOMException;import org.jdom.input.SAXBuilder;public class ClassPathXMLApplicationContext implements BeanFactory {    private Map<String, Object> beans = new HashMap<String, Object>();    public ClassPathXMLApplicationContext() throws Exception {        SAXBuilder sb = new SAXBuilder();        Document doc = sb.build("src/beans.xml"); // 构造文档对象        Element root = doc.getRootElement(); // 获取根元素        List list = root.getChildren("bean");// 先遍历所有的bean,放入beans的map对象中        for (int i = 0; i < list.size(); i++) {            Element element = (Element) list.get(i);            String id = element.getAttributeValue("id");            String clazz = element.getAttributeValue("class");            Object o = Class.forName(clazz).newInstance();            beans.put(id, o);        }        for (int i = 0; i < list.size(); i++) {    //在遍历一次xml文件,将含有property元素的bean的属性bean也装载进来(重复扫描,需要优化)            Element element = (Element) list.get(i);            String id = element.getAttributeValue("id");            String clazz = element.getAttributeValue("class");            Object o = Class.forName(clazz).newInstance();            List list1 = element.getChildren("property");            for (int j = 0; j < list1.size(); j++) {                Element e = (Element) list1.get(j);                String name = e.getAttributeValue("name");                String bean = e.getAttributeValue("bean"); // userDAO                Object object = beans.get(bean);// userDAOImpl的实例                System.out.println(object);                String methodName = "set" + name.substring(0, 1).toUpperCase()                        + name.substring(1, name.length());                Method m = o.getClass().getMethod(methodName,                        object.getClass().getInterfaces()[0]);  //第二个参数是参数类型,即获取该类实现的接口的类型                m.invoke(o, object);                beans.put(id, o);            }        }    }    public Object getBean(String beanName) {        return beans.get(beanName);    }}
该类实现的接口(bean工厂)代码:
package org.zeng.spring;import java.io.IOException;import org.jdom.JDOMException;public interface BeanFactory {    public Object getBean(String beanName);}
总结: spring其中的一个重要核心IOC控制反转(也基本同于与DI依赖注入),大体是说,一些常被用到的类的实现,不需要开发者自己去控制,而全部交给sprintContext(理解为运行环境)去控制。
下面说一说自己理解的好处:
  1. 一般的,一个项目中至少有实体类层(Entity层)、业务逻辑层(Service层)、数据访问层(DAO层)这三层,而每层会有很多类,且这些类的成员属性也可能是其他类。如上代码,UserService下有UserDAO类型的属性,如果按照传统的方法,需要先new UserService()记为对象s,再new UserDAO()记为对象d,然后再将d对象set到对象s中,才能调用d对象的save方法!(很繁琐)。如果将这些类都配置在文件中,每次项目移动时一一的去装载这些类,放入到容器中,什么时候需要,什么时候取就可以了,没必要再new新的对象了。
  2. 面向接口编程时,比如UserDAO的接口是专门操作User对象CRUD的接口,而实现类UserDAOImpl1确可以是mysql,或者是oracle的方式。
    如果要切换数据库环境时,对访问数据层DAO来讲有3种方法:
    (1). 要新增一个对应实现类UserDAOImpl2,并将调用UserDAO1地方全部改为调用UserDAO2。这样就会要改很多处代码,可能增加新的bug,显得并不安全。
    (2). 干脆将UserDAOImpl2的代码完全copy到UserDAOImpl1上替换到原来的内容。即使将原来的内容进行备份,但对整个代码管理角度来讲,这种做法很不优雅。
    (3). 在spring的配置文件中,将对UserDAO1的配置改为UserDAO2的配置(修改bean的路径即可)。这种是最好的做法,很简单,对源代码的改变也是最小的。

附注: 以上列出的是核心代码,还有User实体类,UserDAO接口,及其实现类UserDAOImpl代码,全部源代码在我的github上

0 0