一、深入以下代码分析过程
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
1、通过ClassPathResource获得配置文件封装。
1.1 ClassPathResource为Resource的一种接口实现,可对资源文件进行管理。而Resource接口又是继承java的InputStreamResource,扩展了InputStreamResource的功能,如检查当前资源是否存在,当前资源是否可读等。
1.2 ClassPathResource的getInputStream也可了解一下:
通过class或者classLoader提供的底层方法进行调用
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
} else {
is = this.classLoader.getResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
} else {
return is;
}
}
2、通过XmlBeanDefinitionReader加载资源
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}}
this.reader.loadBeanDefinitions(resource)这一步是资源加载的正真实现。在这步的方法调用中会执行一下代码,这些是关键:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//获取xml文件验证格式
int validationMode = this.getValidationModeForResource(resource);
//获得document对象
Document doc = this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, validationMode, this.isNamespaceAware());
//注册bean
return this.registerBeanDefinitions(doc, resource);
2.1 xml文件格式,了解即可。
代码int validationMode = this.getValidationModeForResource(resource);获取验证模式,就是获取xml文件的格式
DTD:全称为Document Type Definition 是一种xml文件约束语言
XSD:全称为XML Schemas Definition
2.2 获得Document对象
代码Document doc = this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, validationMode, this.isNamespaceAware());获得document对象是封装的DefaultDocumentLoader采用SAX解析方法,源码还是可以看懂的;
另外值得看一下的是this.getEntityResolver()方法,这个方法的作用是给项目本身提供一个寻找DTD声明的方法。验证文件默认的加载方式是通过URL在网络上下载,会造成延迟,用户体验也不好,一般都是讲验证文件放在自己的工程里。
2.3 初步探索解析以及注册BeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// Read document based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
this.registerBeanDefinitions(doc, resource);在这个方法中,重要的目的之一是提取document的root,然后将其作为参数传入registerBeanDefinitions()继续注册。
接下来的代码是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);
}
parseBeanDefinitions(root, delegate);
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)) {
//默认标签的解析方法 形如<bean id="test" Class="ss.java">
parseDefaultElement(ele, delegate);
}
else {
//自定义标签解析 形如<tx:annotation-driven>
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
注:面向设计方法学中常说的一句话,一个类要么被继承,要么急用final修饰。
未完待续 。。。
网友评论