XmlBeanDefinitionReader读取xml配置文件
XmlBeanFactory对DefaultListableBeanFactory进行了扩展,主要是对xml配置文件的读取得到BeanDefinition,对bean的注册和获取,主要是通过继承父类DefaultListableBeanFactory的方法,唯独与父类不同的是,增加了个性化属性XmlBeanDefinitionReader属性,该属性的作用是对xml文档的读取解析
那么对于spring配置文件的读取,解析及注册的主线需要使用的对象如下:
1、Resource:抽象了spring内部所使用到的底层资源,file,class,classPath等
2、ClassPathResource:根据给定的文件地址,返回对应的resource
3、BeanDefinitionReader:定义资源文件获取并转化成相应的BeanDefinition
4、EnvironmentCapable:获取environment的方法,定义了一个getEnvironmen()无参方法
5、DocumentLoader:加资源文件加载并转化为Document文件,其实现类为DefaultDocumentLoader
6、AbstactBeanDefinitionReader:综合了EnvironmentCapable与BeanDefinitionReader功能并进行实现
7、BeanDefinitionDocumentReader:读取document文件转化为beanDefinition对象,并进行注册
8、BeanDefinitionParserDelegate:定义解析Element的方法
XmlBeanDefinitionReader的类继承实现关系如下:
ClassPathResource的类继承实现如下:
通过对以上对象的分析,大致可以弄懂xml配置文件读取的流程。
1、通过ClassPathResource,将给定的路径文件名称,转化成Resource
2、通过XmlBeanDefinitionReader中属性 DocumentLoader将resource加载并转化为Document文件
3、通过接口BeanDefinitionDocumentReader的实现DefaultBeanDefinitionDocumentReander包含的BeanDefinitionParserDelegate对象对document进行解析,转化为beanDefinition对象进行注册,注册就是放入一个map中,Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
现在根据下面代码debug进入,看看XmlBeanDefinitionReader类,内部做了什么
Resource resource= new ClassPathResource("beanFactoryTest.xml");
BeanFactory bf = new XmlBeanFactory(resource);
跟踪进入下列方法
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
在此处调用了父类的构造方法
public AbstractAutowireCapableBeanFactory() {
super();
/**
* 忽略给定接口的自动装配功能
* 为什么要这么做呢?
* 当A中有属性B,当spring在获取A的属性B的时候,B还没有初始化,那么spring会初始化B实例,但是在某些情况下,spring不会初始化B,如B实现了BeanNameWare接口,
* spring给的介绍为:
* 自动装配时,忽略给定的依赖接口,典型的应用是通过其他方式解析Application上下文注册依赖,类似于BeanFactory通过BeanFactoryWare进行诸如
* 或者ApplicationContext通过ApplicationContextWare进行注入
*/
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
到达XmlBeanDefinitionReader的loadBeanDefinitins(resource)方法
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);
}
//通过属性记录已经加载的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//2、获取输入流,从resouce中获取inputStream,并构造inputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//3、通过resource及构造的inputSource调用,进入核心逻辑doLoadBeanDefinitions
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();
}
}
}
spring中,很多真正干事情的方法函数,前面都带有do,进入XmlBeanDefinitionReader的doLoadBeanDefinitions方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//1、对xml进行验证 2、加载document 返回doccument对象
Document doc = doLoadDocument(inputSource, resource);
//3、根据返回的document注册bean信息
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
上面的第一步,对xml进行验证
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
其中的getValidationModeForResource方法
protected int getValidationModeForResource(Resource resource) {
//如果通过XmlBeanDefinitionReader 的setValidationMode设置了验证模式,则使用设置的验证模式
/**
* 配置xml文件分为XSD(XML Schemas Definition)和DTD(Document Type Definition)
* <!DOCTYPE beans PUBLIC "-//Sp口 ng//DTD BEAN 2 0//EN" dtd/ Spring-beans-2 O.dtd">
*
* <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">
*
*
* 两种类型可以根据是否包含DOCTYPE来判断是DTD 还是XSD模式
*/
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//如果没有设置,则自动检测
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
return VALIDATION_XSD;
}
使用DefaultDocumentLoader的loadDocument方法将resource转为document文件
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
这个转换过程没有什么特殊的,感兴趣的话可以进去,现在跳过,主要看看XmlBeanDefinitionReader中的registryBeanDefinitions方法
//解析及注册BeanDefinition
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//使用默认的DefaultBeanDefinitionDocumentReader 初始化BeanDefinitionReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录统计前BeanDefinition的个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载bean,这个地方是重点
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//本次加载的BeanDefinition的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
进入DefualtBeanDefinitionDocumentReader类的registryBeanDefinitions方法中
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//提取root
Element root = doc.getDocumentElement();
//核心部分,真正开始解析
doRegisterBeanDefinitions(root);
}
现在我们要进入真正的解析过程了,前面的步骤都是准备阶段,进入DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法中
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
//创建BeanDefinitionParserDelegate对象
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//处理profile属性
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)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析前的处理,留给子类实现 使用了模板模式方法
preProcessXml(root);
//解析
parseBeanDefinitions(root, this.delegate);
//解析后的处理,留给子类实现
postProcessXml(root);
this.delegate = parent;
}
进入parseBeanDefinitions方法中
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//默认命名空间进行处理
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)) {
//对各种默认的标签进行处理,比如 alias,import,bean,beans等spring中定义的标签
parseDefaultElement(ele, delegate);
}
else {
//自定义命名空间进行处理 比如使用aop需要在配置文件中使用的<aop:aspectj-autoproxy/> aop标签
delegate.parseCustomElement(ele);
}
}
}
}
else {
//自定义命名空间进行处理
delegate.parseCustomElement(root);
}
}
进入DefaultBeanDefinitionDocumentReader的parseDefaultElement方法,对默认标签的解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//对import标签进行处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//对alias标签进行处理
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的解析过程,进入DefaultBeanDefinitionDocumentReader的processBeanDefinition(ele,delegate)方法中
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//1、通过委托BeanDefinitionParserDelegate类的parserBeanDefinitionElement()方法对ele进行解析,返回的BeanDefinitionHolder
//实例bdHolder,实例bdHolder中包含class,name,id ,alias等属性
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//2、bdHolder默认不为空时,bdHolder中包含自定义的属性,对自定义的属性进行解析
/**
* 适用的场景如下
* <bean id="test" class="test.MyClass">
* <myBean:user username="aaa"></myBean:user>
* </bean>
*
* 如果默认标签下还存在自定义的标签,需要对自定义的<myBean></myBean>标签进行解析
*/
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//3、解析完成之后对bdHolder进行注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//4、最后发出响应事件,通知相关的监听器
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
processBeanDefinition方法的主要操作如下:
1、通过BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法对ele进行解析,并返回包含BeanDefinition,alias,name ,id等属性的BeanDefinitonHolder对象
2、如果在默认标签下还存在自定义的子标签,则需要对子标签进行解析,也放入到BeanDefinitionHolder中
3、通过BeanDefinitionReaderUtils工具类的registerBeanDefinition方法将beanDefinitionHolder进行注册
4、最后发出响应事件,通知相关的监听器,相关的bean已经注册成功了