在 Spring 框架中,xml 配置文件的读取是 Spring 中重要的功能,因为 Spring 的大部分功能都是以配置作为切入点的。Spring 通过对配置文件的读取,为我们管理所有的 bean 对象。对于代码
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(“spring-aop-aspectj.xml”)); 本文主要讲解对 new ClassPathResource(“spring-aop-aspectj.xml”) 的实现,Spring 在创建 ClassPathResource 的实例对象的时候到底对配置文件做了哪些事情?
对于new ClassPathResource("spring-aop-aspectj.xml") 返回的是 Resource 的实例对象。在 XmlBeanFactory 中:
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
对于 super(parentBeanFactory) 主要做的事情是为基类 AbstractBeanFactory 中的全局对象 private BeanFactory parentBeanFactory 赋值;所以对于配置文件的认证解析主要在 this.reader.loadBeanDefinitions(resource) 中实现。而对于文件的加载是在 XmlBeanFactory 父类 XmlBeanDefinitionReader 中实现,如下(只保留关键代码):
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//①通过属性来记录已经加载的资源
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 {
//在 encodedResource 获取已经封装的 Resource 对象并且该对象中获取封装的 inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
//②设置编码格式
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
}catch (IOException ex) {
...
}finally {
...
}
}
//③处理各种异常
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
catch (Exception ex) {
...
}
}
//④
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录统计前 BeanDefinition 的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
//⑤加载以及注册 bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//本次 BeanDefinition 的加载个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
}
①处主要是通过属性来记录已经加载的资源并进行验证是否该资源已经被加载,如果加载了则不反复加载。②处对于 encodedResource 对象,如果设置有自定义的编码格式,则根据设置的编码格式设置 encodedResource 的编码,如果没有则使用默认的编码格式。这两步是对配置文件的第一次鉴权。③处的代码主要是对各种异常的处理,根据不同的异常输出不同异常信息。在 ④ documentLoader 的实现是 DefaultDocumentLoader(在下方) ,其中主要注意的是 getValidationModeForResource(resource) 会根据该方法返回的参数假设为: validationMode ,根据 validationMode 判定该配置文件的认证模式到底是 DTD 还是 XSD 并根据认证结果对配置文件进行解析。⑤在这个方法中很好的应用了面向对象中单一职责的原则,讲逻辑处理委托给单一的类进行处理,而这个逻辑处理类就是 BeanDefinitionDocumentReader,其实例的真正实现是DefaultBeanDefinitionDocumentReader(在下方),这个类的主要作用就是提取 Element root ,以便于再次将 root 作为参数继续 BeanDefinition 的注册。
对于 DefaultDocumentLoader(只保留关键代码) 如下:
public class DefaultDocumentLoader implements DocumentLoader {
//①
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
...
}
}
}
return factory;
}
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
}
在 DefaultDocumentLoader 中只是简单的返回一个 DefaultDocumentLoader 实例,并设置相应的参数。当调用完①处的方法 loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) 时,已经返回一个 Document 的对象了,
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
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)) {
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(root, this.delegate) 中对元素中的 bean 处理。