您的位置:首页 > 编程语言 > Java开发

spring Ioc源码解读-xml资源加载与解析

2017-11-30 21:03 761 查看
Spring对内部使用到的资源比如spring.xml实现了自己的抽象结构,Spring利用Resource接口封装底层资源文件

public interface Resource extends InputStreamSource {

boolean exists();

boolean isReadable();

boolean isOpen();

URL getURL() throws IOException;

URI getURI() throws IOException;

File getFile() throws IOException;

long lastModified() throws IOException;

Resource createRelative(String relativePath) throws IOException;

String getFilename();

String getDescription();

}


Resource接口继承了InputStreamSource接口

public interface InputStreamSource {

InputStream getInputStream() throws IOException;

}


这个接口封装任何能够返回InputStream的类,它只有一个方法getInputStream()该方法返回一个新的InputStream对象,Resource接口对不同来源的资源文件都有相应的Resource实现

FileSystemResource

ClasspathResource

UrlResource

InputStreamResource

ByteArrayResource

有时候我们需要读取classpath路径下的一个文件进行操作,我们可以使用spring提供的ClassPathResource类读取文件

Resource resource = new ClassPathResource("spring.xml");
InputStream in = resource.getInputStream();


当Resource相关实现类完成了对资源的封装后,我们可以将配置文件的读取工作交给XmlBeanDefinitionReader来处理

XmlBeanFactory调用如下这个构造函数进行初始化

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}


XmlBeanFactory依赖关系如下所示



在XmlBeanFactory构造方法中调用了父类DefaultListableBeanFactory的构造方法,而后DefaultListableBeanFactory又调用了AbstractWutowireCapableBeanFactory的构造方法

public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}


ignoreDependencyInterface方法的功能是忽略给定接口的自动装配功能.比如说,当A中有属性B的时候,那么当Spring在获取A的Bean的时候,如果其属性B还没有初始化,那么Spring会自动初始化B,但是如果B实现了BeanNameAware,BeanFactoryAware,BeanClassLoaderAware接口,spring会忽略对其自动装配,而是通过其他途径对其进行装配

XmlBeanFactory调用完super()父类构造方法后,继续调用this.reader.loadBeanDefinitions(resource);方法加载资源

加载资源是会调用如下的一个方法,

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
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);
}
}


这个方法非常的冗长,核心代码try包围的三行,三行代码主要的目的如下

1. 获取对xml文件的验证模式

2. 加载xml文件,并得到相应的Document

3. 根据返回的Document注册bean信息

第三项是注册bean的重点

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//beanMap中已有的bean个数
int countBefore = getRegistry().getBeanDefinitionCount();
//注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次注册bean的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}


BeanDefinitionDocumentReader是一个接口用于bean的注册

在本例中调用的是BeanDefinitionDocumentReader的实现之一DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法如下

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);
}


首先获取了root节点用于以便再次将root作为参数继续BeanDefinition的注册,而delegate用于解析xml中定义的各种属性

比如

public static final String SCOPE_ATTRIBUTE = "scope";

public static final String SINGLETON_ATTRIBUTE = "singleton";

public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";


随后parseBeanDefinitions(root, delegate);开始解析bean的定义了

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);
}
}


spring中标签有两种,一类是默认的标签,如

<bean id="test" class="com.spring.aop.test1.TestBean"/>


另一类是自定义标签如

<aop:aspectj-autoproxy/>


那么spring如何确认标签是自定义的还是默认的标签呢?spring依赖固定的命名空间http://www.springframework.org/schema/beans进行对比,如果一致就是默认否则就是自定义.对这两种标签的解析采用了不同的方式,将在接下来的文章中讲解
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: