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加载的大体流程就是这样,理解了基本的原理后,再阅读源码就会轻松一些了。

关注我的微信公众号,观看更多精彩内容:
spring bean加载原理_第1张图片

你可能感兴趣的:(【spring】,Spring历程)