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

简单模拟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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值