上面一篇提到了bean加载的入口:
AbstractRefreshableApplicationContext的refreshBeanFacotry中有一行代码:
loadBeanDefinitions(beanFactory);
当前的applicationcontext的实现类是xmlwebapplicationContext,所以最终的loadbeanDefinitions(beanFactory)
方法的实现是在xmlwebapplicationContext中:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = XBeanHelper.createBeanDefinitionReader(this, beanFactory, xmlPreprocessors);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
很容易就能发现,bean的加载的是通过最后一行实现的。继续追踪进入源码:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
现在知道为什么可以在web.xml中配置spring的时候指定多个配置文件了吧?哈哈。
继续追踪XmlBeanDefinitionReader这个类。这个类继承了AbstractBeanDefinitionReader。AbstractBeanDefinitionReader还有一个实现类:PropertiesBeanDefinitionReader。
从名字很容易可以看出spring能支持基于xml和基于properties文件的bean加载方式。XmlBeanDefinitionReader
过把xml加载到一个Document对象里面,然后通过BeanDefinitionDocumentReader来分析这个document对象。
BeanDefinitionDocumentReader的默认实现类是:DefaultBeanDefinitionDocumentReader。
其中注册bean的代码如下:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
preProcessXml(root);
parseBeanDefinitions(root, delegate);
postProcessXml(root);
}
spring在这里提供了xml配置文件解析的两个扩展接口:preProcessXml,postProcessXml. 用户可以根据自身需求
扩展这两个接口。下面就关注一下上面代码里面加载bean的核心方法:parseBeanDefinition.
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(delegate.getNamespaceURI(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;
String namespaceUri = delegate.getNamespaceURI(ele);
if (delegate.isDefaultNamespace(namespaceUri)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
从上面代码可以看出,xml配置文件里面的<beans></beans>标签里面的闭合标签被解析成一个个Node.然后
调用每个标签的NameSpaceHandler进行标签的解析,并注册相应的bean。比如:解析<aop:config></aop:config>
这种标签时会调用AopNamespaceHandler来进行解析。对于使用默认NameSpace的标签,又分为import,alias,bean三个标签进行解析并注册相应的bean。
至此,bean的注入的轮廓基本清晰了。有兴趣的朋友可以沿着这个思路继续深入一些细节,比如aop的注入等等。