您的位置:首页 > 编程语言 > Java开发

Spring源码解读 IOC容器初始化

2017-03-07 21:41 597 查看
前言

自己对IOC容器和Spring容器理解是不一样,在我看来IOC容器的基本功能就是获取Bean,没有其它功能。而spring容器就是在IOC容器基础上扩展了许多功能,例如生命周期

的管理,支持不同的信息源,应用事件等等。而本篇文章主要就是来分析IOC容器的初始化过程,通过分析这一过程让我们了解,我们定义好bean之后,spring内部做了些什

么。分析源码的版本是spring4.1.7。
正文

在分析之前先看一下配置文件和代码,都是最简单的。凡是都是由浅入深吗。

配置文件

<bean id="user" class="vo.User">
<property name="name" value="czy" />
</bean>


实体类

package vo;

public class User {
private String name;

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}


测试代码

ApplicationContext  ctx = new ClassPathXmlApplicationContext("spring.xml");
User u = (User) ctx.getBean("user");
System.out.println(u.getName());


整个配置和代码都非常简单,自己觉得对于了解其原理的话,足够了。

从上面的代码中可以猜到IOC容器的初始化过程,在ClassPathXMLApplicationContext的构造方法中。调试代码,发现确实如此。

先看一下ClassPathXmlApplication的构造方法,

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {

super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}


setConfigLocations()方法主要是讲我们传入的参数"spring.xml"设置进入,没什么可说的。看来主要逻辑在refresh()方法中。refresh()方法如下:

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1. 刷新前的准备工作
prepareRefresh();
//2. 关闭释放旧的beanFactory创建新的beanFactory,读取配置文件等
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3. 对beanFactory进行一些基本的初始化
prepareBeanFactory(beanFactory);

try {
//4. 下面两行主要用户扩展,处理所有已注册的BeanFactoryPostProcessor,实现在已经加载配置但未初始化bean时对配置进行修改
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
//5. 处理所有已注册的BeanPostProcessor,主要用于扩展,实现bean初始化前后的一些定制操作
registerBeanPostProcessors(beanFactory);

//6. 初始化消息源bean
initMessageSource();
//7. 初始化事件监听器集,也有人叫事件监听器的注册表,所有的事件监听器都在这个bean里进行管理
initApplicationEventMulticaster();
//8. 主要用于扩展,实现一些特殊bean的初始化,时间点是类似消息源事件监听器集等特殊bean初始化后,普通的bean初始化前
onRefresh();
//9. 注册监听器
registerListeners();
//10. 初始化其余的非延迟加载的单例bean
finishBeanFactoryInitialization(beanFactory);

//11. 刷新完成调用LifecycleProcessor的onRefresh方法,并且发布ContextRefreshedEvent事件
finishRefresh();
} catch (BeansException ex) {
// 销毁已经创建的单例bean
destroyBeans();
// 重新设置active标记
cancelRefresh(ex);
throw ex;
}
}
}


整个方法有很多功能,分别针对spring容器各个功能进行初始化,而本篇文章要分析的IOC容器的初始化在方法过程2,obtainFreshBeanFactory()方法中,在该方法中有调

用了refreshBeanFactory(),从名字也可以看出来初始化IOC容器的过程在这个方法里。

protected final void refreshBeanFactory() throws BeansException {
//1.如果已经有了beanFactory则销毁所有的bean,并关闭beanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//2.创建BeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//3.设置BeanFactory的Id,ID是个弱引用。
beanFactory.setSerializationId(getId());
//4.应用上下文使用的自定义BeanFactory
customizeBeanFactory(beanFactory);
//5.解析我们定义的bean,并添加到beanFactory
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}


在看loadBeanDefitions()方法

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为指定的BeanFactory创建了一个新的XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// 设置beanDefinitionReader的参数
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

//初始化BeanDefitionReader
initBeanDefinitionReader(beanDefinitionReader);
//通过beanDefitionReader解析我们定义的bean
loadBeanDefinitions(beanDefinitionReader);
}


又是一个loadBeanDefitions()方法,不过这次参数的类型是XmlBeanDefinitionReader。在看这个方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取资源,然后再次调用XmlBeanDefinitionReader的loadBeanDefinitions()方法
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}


还是loadBeanDefitions()方法,不过这里是调用XmlBeanDefitionReader的。我调试的时候走的是下面的那个逻辑,在看下面的那个loadBeanDefitions()方法。中间

还有很多简单的loadBeanDefitions()方法,在此不表,在看下面俩个主要的loadBeanDefitions()方法

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}

if (resourceLoader instanceof ResourcePatternResolver) {
// 将我们定义的文件通过ResourceLoader转换成Resource
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}

Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//获取resource的流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

漫无止境的loadBeanDefitions方法终于结束了,突然想到了动漫凉宫春日的忧郁里漫无止境的八月的剧情,如果动漫里那么多次八月的循环只是春日为了换回阿虚的一次挽

留,那么这么多漫无止境loadBeanDefitions()的调用就是为了获取我们定义bean文件的流。好像用如果...那么不合适,不过不要紧反正都是自以为。

说了一些有的没的,回到正题,loadBeanDefitions()里出现了Resource,ResourceLoader,ResourcePatternResoulver。

Resource就是spring对我们定义bean文件的抽象,里面声明了获取InputStream的方法。
ResourceLoader定义了如何通过路径抽象成Resource

ResourcePatternResoulver 实现了ResourceLoader,可以传入一个多个路径。

总结一下IOC容器初始化的第一步就是通过ResourceLoader根据文件路径转换成Resource,然后通过Resource获取这个文件的流。

在看一下doLoadBeanDefitions()方法,主要代码如下:

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

将IO流,解析成document文档,然后再看一下registerBeanDefitions()方法,
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

声明一个BeanDefitionDocumentReader来解析document文档,再看一下doucmetReader的registerBeanDefitions()方法,
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}

获取文档的根元素,然后通过解析根元素,再看一下doRegisterBeanDefitions()方法,
protected void doRegisterBeanDefinitions(Element root) {
//BeanDefinitionParserDelegate里包含了spring bean定义规则的处理,
//用它来讲document文档,解析成spring内部使用的数据结构BeanDefition
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);

if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}

preProcessXml(root);
//将document文档解析成BeanDeftions的过程。
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);

this.delegate = parent;
}

再看一下parseBeanDefitons()方法
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//解析默认的元素
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

再看一下parseDefaultELement()方法,
//处理导入的文件
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//解析别名
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//解析Bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//解析Beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
看一下如何解析Bean

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//将元素解析成BeanDefinitonHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//将BeanDefitinionHolder注入容器
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

在这里说明一下BeanDefinitionHolder是BeanDefinition的封装,封装了BeanDefition,bean的名字和别名。而BeanDefition就是spring就是我们定义的各种bean在spring内部的数据结构。
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {

this.parseState.push(new BeanEntry(beanName));
//获取Bean中设置的class的名字,载入到BeanDefitions中
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}

try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//生创建BeanDefition对象,为Bean定义信息的载入做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//对当前Bean元素进行属性解析,并设置到descrition
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析Bean的各种元素
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析bean的构造函数
parseConstructorArgElements(ele, bd);
//解析bean的属性
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);

bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));

return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}

return null;
}

在这里个人对spring IOC容器的初始化的研究就结束了,至于具体怎么解析bean的各种元素,个人认为不重要,无非就是各种解析XML的规则而已。而整个spring IOC容器加载的过程与结果我认为自己已经了解了。最后做一下总结。
IOC容器初始化主要过程

1.通过ResourceLoader将文件路径转换成Resource。

2.通过Resource获取InputStream

3.通过BeanDefinitionDocumentReader将IO流转换成Document

4.通过BeanDefinitionParserDelegate 把文档中的元素解析成BeanDefinition并放到BeanDefinitionHolder中。

5.将BeanDefinition注册到BeanFactory中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息