我们先来看一段代码
@Test
public void testAbc() throws IOException {
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
Person person = (Person) bf.getBean("person");
person.say();
}
applicationContext.xml
文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="person" class="com.yao.Person"></bean>
</beans>
这段代码非常简单也非常经典,spring是如何解析xml文件将bean注入到spring容器的?过程是什么样的?我们接下看看spring是如何做的,至于是如何将xml解析成Document的,这里先不做过多解释,后面有章节单独介绍?
我们进入XmlBeanFactory
的构造函数中跟进一下可以看到如下代码
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource the XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
//解析resource,并将resource解析出的Bean会封成Spring的BeanDefinition,然后在将
//每一个BeanDefinition在注册到Spring容器中完成初始化
this.reader.loadBeanDefinitions(resource);
}
继续跟进loadBeanDefinitions(resource)
方法会看到如下
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//new EncodedResource()完成编码集设置的工作
return loadBeanDefinitions(new EncodedResource(resource));
}
继续:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
//准备加载BeanDefinitions
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
...
}
继续,XmlBeanDefinitionReader#doLoadBeanDefinitions
的核心到了,就是这里的两个
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//完成xml文件读取并转成Document对象,Document对象就包含了xml节点中的所有节点数据
Document doc = doLoadDocument(inputSource, resource);
//将读取出来的Document对象进一步解析成Spring的BeanDefinitions并注册到spring容器,完成初始化
int count = registerBeanDefinitions(doc, resource);
...
return count;
}
...
}
二、doLoadDocument(inputSource, resource)
读取xml文件,并最终返回一个Document对象来表示xml里所有元素。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
这里需要说明一下两个参数:
getEntityResolver()
记着一点就是该方法免去了从网络上获取dtd或xsd的声明,因为你的xml写的规范不规范是需要dtd和xsd的声明文件做约束的。因为网络是不可靠的,不可需要验证的时候从网络上获取dtd或xsd头文件。所以此参数的作用就是通过此链接地址作为key到对应的文件中去找相应的dtd或xsd文件,这样子就避免了取网络下载,现在这个dtd或xsd文件相应的模块中都有,我们不用担心不存在,而且是越往后的版本里都包含了之前版本的dtd或xsd文件声明。
getValidationModeForResource(resource)
记住一点就是这个是根据返回的resource中内容中,通过内容判断我们配置的applicationContext到底是xsd格式还是dtd格式。
判断的内容依据就是判断我们声明的xml中有没有对应的schema文件声明,并用publichId和SystemId组合表示。比如:判断声明如果有DOCTYPE
则就是dtd验证,反之就是xsd验证。
如何从inputSource中读取文件并解析成docuemnt,这里就不说赘述了,有兴趣的朋友可以看下源码,挺简单的。
三、registerBeanDefinitions(doc, resource)
registerBeanDefinitions()方法完成了java bean到 spring bean的一个转换。看看如何做的
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
/**
* createBeanDefinitionDocumentReader,此方法会实例化BeanDefinitionDocumentReader接口,使用
* BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader,最后该实现类的
* registerBeanDefinitions()方法完成了BeanDefinitions的注册,
*/
//1.BeanDefinitionDocumentReader读取器,用于读取解从xml解析出来的Domcument,用BeanDefinition封装起来
//这里只是封装,还未注册到spring容器。
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//2. 这一步获取注册前容器已有的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
//3. 这一步才是真正的将读取出来的document对象,封装成的BeanDefinition注册到Spring容器,
//registerBeanDefinitions()方法使用的的BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中的
//
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这个时候我们看createBeanDefinitionDocumentReader()这个方法一直跟下去会看到XmlBeanDefinitionReader类中有如下
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
查看DefaultBeanDefinitionDocumentReader类会找到最终如下方法
//默认标签的解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//我们看这个解析Bean标签
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
processBeanDefinition(ele, delegate)
如下,此方法中完成了Spring Bean的注册
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//将从document中解析出来的java Bean 封装成spring的BeanDefinition
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//将封装好的spring BeanDefinition注册到spring容器中,完成Spring bean的初始化
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));
}
}
四、delegate.parseBeanDefinitionElement(ele)
我们查看spring是如何将java Bean封装成spring的Bean的
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
...
//我们看parseBeanDefinitionElement
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
...
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
...
try {
//new了一个GenericBeanDefinition,来封装java bean,
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//完成属性的解析
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析子元素meta
//元数据使用如下:
//<bean name="person" class="com.yao.Person">
// <meta key="age" value="18"/>
//</bean>
//元数据的解析
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//构造函数的解析
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
//最后返回bd
return bd;
}
...
return null;
}
我们可以看到Spring容器中的BeanDefinition实际都是GenericBeanDefinition类型的。至此Spring的初始化完成了。
网友评论