spring bean加载原理

简单的分析了一下spring bean的加载原理,属于个人的理解,源码比这个要复杂的多:

spring的配置文件applicationContext.xml的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- 用户dao类,就是一个普通的pojo,里面有个addUser方法,调用会输出一行字 -->
    <bean id="userDao" class="com.qjl.study.spring.dao.impl.UserDaoImpl"></bean>
</beans>

非web环境下我们通常这么来加载IOC容器,获取bean:

BeanFactory ac = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserDao userDao = ac.getBean("userDao");

所以,我简单的实现了一下这个BeanFactory接口和ClassPathXmlApplicationContext类(不过一般上是用ApplicationContext这个接口来接收ClassPathXmlApplicationContext,不过就本例来说没有太大的区别)。
BeanFactory接口的代码如下:

package com.qjl.study.spring.factory;

/**
 * 类名称: bean工厂
 * 类描述: 实例化各种bean
 * 全限定性类名: com.qjl.study.spring.factory.BeanFactory
 * @author MrQJL
 * @date 2018年1月3日 下午9:46:30
 * @version V1.0
 */
public interface BeanFactory {

    /**
     * 通过bean的id获取实例化的bean对象
     * 获取bean的时候,该bean可能不存在,所以要抛出异常
     * @param bean的名称
     * @return 实例化的bean对象
     */
    Object getBean(String name) throws Exception;
}

我在BeanFactory里面就写了一个getBean(String name)方法,用于输入bean的id,返回对应的实例,因为输入的bean的id可能不存在,所以要抛出异常。
ClassPathXmlApplicationContext类实现了BeanFactory接口,代码如下:

package com.qjl.study.spring.factory.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import com.qjl.study.spring.factory.BeanFactory;

/**
 * 类名称: 上下文对象
 * 类描述: 用于获取编译路径下xml文件所对应的bean的IOC容器
 * 全限定性类名: com.qjl.study.spring.factory.impl.ClassPathXmlApplicationContext
 * @author MrQJL
 * @date 2018年1月3日 下午10:04:50
 * @version V1.0
 */
public class ClassPathXmlApplicationContext implements BeanFactory {

    /**
     * 这就是那个IOC容器
     */
    private Map<String, Object> beans = new HashMap<String, Object>();

    public ClassPathXmlApplicationContext(String configLocation) throws Exception {
        // 使用jdom的SAXBuilder读取xml文件
        SAXBuilder sb = new SAXBuilder();
        // 加载xml文档进内存
        Document doc = sb.build(this.getClass().getClassLoader()
            .getResourceAsStream(configLocation));

        // 获取根节点--也就是beans
        Element root = doc.getRootElement();
        // 获取根节点的孩子节点--也就是bean
        @SuppressWarnings("unchecked")
        List<Object> childList = root.getChildren("bean");
        // 循环取出每一个bean节点以及他们的id和class属性,利用反射创建一个对象
        for (int i = 0; i < childList.size(); i++) {
            Element child = (Element) childList.get(i);
            // 获取id属性
            String id = child.getAttributeValue("id");
            // 获取class属性
            String clazz = child.getAttributeValue("class");
            // 通过反射加载类,实例化bean对象
            Object obj = Class.forName(clazz).newInstance();
            // 将实例化的对象放入IOC容器(map)中
            beans.put(id, obj);
        }
    }

    @Override
    public Object getBean(String name) {
        return beans.get(name);
    }
}

下面就从这个ClassPathXmlApplicationContext的构造函数开始说起:

1.读取spring的配置文件

调用ClassPathXmlApplicationContext的有参构造方法,传入配置文件的名称。
调用SAXBuilder的build方法读取配置文件

// 使用jdom的SAXBuilder读取xml文件,也可以使用dom4j,SAX读取xml配置文件
SAXBuilder sb = new SAXBuilder();
// 加载xml文档进内存,configLocation就是传入的文件名,Document是jdom包里面的类
Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream(configLocation));
// 获取根节点,也就是beans
Element root = doc.getRootElement();
// 获取根节点的孩子节点,也就是bean
List<Object> childList = root.getChildren("bean");

2.反射加载类,实例化对象,并将对象放入IOC容器

// 循环取出每一个bean节点以及他们的id和class属性,利用反射创建一个对象
for (int i = 0; i < childList.size(); i++) {
    Element child = (Element) childList.get(i);
    // 获取bean标签的id属性
    String id = child.getAttributeValue("id");
    // 获取bean标签的class属性
    String clazz = child.getAttributeValue("class");
    // 通过反射加载类,实例化bean对象
    Object obj = Class.forName(clazz).newInstance();
    // 将实例化的对象放入IOC容器(map)中
    beans.put(id, obj);
}

3.通过getBean从IOC容器获取对象

@Override
public Object getBean(String name) {
    return beans.get(name);
}

至此,构造函数内的业务逻辑执行完毕,配置文件中配置的bean都加载并实例化完毕,调用getBean方法获取对应的实例对象即可。

spring bean加载的大体流程就是这样,理解了基本的原理后,再阅读源码就会轻松一些了。

猜你喜欢

转载自blog.csdn.net/a909301740/article/details/78985658