首先,要明白,ApplicationContext扩展了ResourceLoader,它和XmlBeanFactory不一样,在容器启动的时候,不需要向ApplicationContext传入Resource,只需传入Spring的资源文件名,ApplicationContext会自动加载资源文件名对应的Resource。并且你可以同时配置多个资源文件一起传给ApplicationContext。
在容器启动时,开始了容器中Bean的注入过程,具体详细注入过程,网上一大堆,为大家推荐个java eye中比较经典的IOC注入分析吧,网址是 :
http://www.iteye.com/wiki/Spring-source/1226-Spring源代码解析(一):IOC容器
在此,我主要给大家分析下在对资源文件中属性Bean的详细解析过程。
spring中xml资源的解析,主要是由XmlBeanDefinitionReader完成,该类是在ApplicationContext容器启动时初始化的。解析的详细过程是BeanDefinitionParserDelegate代理完成的。
BeanDefinitionParserDelegate中parseBeanDefinitionElement方法:
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* <code>null</code> if problems occured during the parse of the bean definition.
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
/*
* parserState是一个栈,里面只能存放ParseState.Entry类型的元素,所以,要想将String类型的beanName存入栈中,\
* 必须将beanName封装成Entry元素
*/
this.parseState.push(new BeanEntry(beanName));
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);
}
/*
* 其实就是new一个BeanDefinition实例,并赋值此BeanDefinition的className以及parent属性值
*/
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
/*
* 对"<id="" name="" scope="" ..>"配置形式进行解析,解析出该Bean的一些生命周期、
* 是否延迟加载、自动装配方式等属性值,并且赋值给上面生成的BeanDefinition实例
*/
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//解析Bean的"<description ../>"属性
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
//解析"<look-up ..>"形式的配置,得出的值存入的先前生成的Beandefinition的methoOverrides属性中
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析"<replace ..>"形式的配置,得出的值存入的先前生成的Beandefinition的methoOverrides属性中
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
/*
* spring中的Ioc注入有三种方式:构造方法注入、set方法注入和接口注入,以下则为
* 几种注入方式的代码
*/
//构造方法注入处理
parseConstructorArgElements(ele, bd);
//set方法注入的处理(比较常用的注入)
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;
}
下面,将分析下set注入形式的解析处理,该处理是parseBeanDefinitionElement方法中完成的,具体如下:
/**
* Parse property sub-elements of the given bean element.
*/
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {
//解析主要方法
parsePropertyElement((Element) node, bd);
}
}
}
parsePropertyElement(Element ele, BeanDefinition bd)方法代码如下:
/**
* Parse a property element.
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//解析<proiperty name=..>中name属性值
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果该BeanDefinition中已经存在了一个与此name相同的属性值,则抛出异常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析该propertyName对应的值,也许只是一个普通的string,也许是个List列表,也许是该BeanDefinition依赖的一个Bean(对应"ref="形式的配置)
Object val = parsePropertyValue(ele, bd, propertyName);
//封装成PropertyValue
PropertyValue pv = new PropertyValue(propertyName, val);
//解析原始元素
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
/*
* 将解析完成的PropertyValue存入该BeanDefinition的PropertyValues中,
* 有两个作用: 1.容器启动后,加载该BeanDefinition对应的Bean时,需要设置该Bean的属性值
* 2.避免一个BeanDefiniton有多个相同的propertyName
*/
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
都说程序员学习三部曲:what how why. 其实,我们在知道使用、知道配置spring的时候,我们应该还有尽自己最大努力做到 why。知其然,也要知其所以然。
分析源码是痛苦的,但对于初级程序员来说,收获也是巨大的,我们不仅可以更深一层次的掌握java技术,而且可以学学高手的编码规范以及重构技术。