本章重点
- ant方式的资源定位
- 加载XML文件
- 解析XML文件
1. 资源定位
org.springframework.core.io.Resource 为 Spring 框架所有资源的抽象和封装,它继承 org.springframework.core.io.InputStreamSource接口。Spring装载bean的过程也就是:xml---->Resource---->解析xml---->注册bean。
常见的代码片段有如下几种:
- 单个文件加载:
ClassPathResource resource = new ClassPathResource("mybean.xml"); // 获取配置资源
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 实例化BeanFactory
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // 实例化资源解析器
reader.loadBeanDefinitions(resource); // 加载资源
MyBean myBean = (MyBean) factory.getBean("mybean");//获取自己定义的bean
- 多个文件加载
PathMatchingResourcePatternResolver patternResolver=new PathMatchingResourcePatternResolver();
Resource[] resources=patternResolver.getResources("classpath*:*.xml");//后面会讲为啥不是classpath:*.xml
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // <2>
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // <3>
reader.loadBeanDefinitions(resources);
或
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mybean.xml");
MyBean myBean = (MyBean) applicationContext.getBean("mybean");
单个文件容易理解,主要看下多个文件是如何定位追踪的。
路径 | 跟路径 | 子路径 |
---|---|---|
classpath:config/spring/spring-*.xml | classpath*:config | spring/spring-.xml |
classpath:config/springmvc/spring-.xml | classpath*:config/springmvc | spring-*.xml |
Spring 源码实现过程其实就是两个函数的递归调用:
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {// 1)常量,其实就是 "classpath*:";
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {// 2)匹配器是AntPathMatcher;判断是否含有*和?通配符
return findPathMatchingResources(locationPattern);
}
else {
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));//3) 返回该目录下的所有文件,封装为Resource数组
}
}
else {
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
return findPathMatchingResources(locationPattern);
}
else {
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
- 1)常量CLASSPATH_ALL_URL_PREFIX,固定值 "classpath*:";
- 2)getPathMatcher()返回AntPathMatcher,isPattern判断字符串中是否含有 *和?通配符
- 3) findAllClassPathResources 返回该目录下的所有文件,并封装为Resource数组
获取匹配路径的配置文件:
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);//递归调用
Set<Resource> result = new LinkedHashSet<Resource>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));// 1)虚拟文件系统里的文件
}
else if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));// 2) jar包里的文件
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));// 3)拿到所有匹配的路径表达式的文件
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[result.size()]);
}
- 1 ) 加载虚拟文件系统里的配置文件
- 2 ) 拿到jar包里的所有匹配的路径表达式的文件
- 3 ) doFindPathMatchingFileResources拿到当前根目录下所有匹配的路径表达式的文件
2.加载XML文件
加载XML文件:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));//1)
}
- loadBeanDefinitions 执行加载bean操作
// 1)当前线程,正在加载Resource的集合
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>("XML bean definition resources currently being loaded");
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.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {// 2)将正在加载的文件放入缓存中
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream(); //3)获取文件字节流
try {
InputSource inputSource = new InputSource(inputStream);//4)封装
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());// 5)设置字符集
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());//6)加载 BeanDefinition,真正加载配置文件的方法
}
finally {
inputStream.close();// 7)关闭流
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove(); 8)处理完移除
}
}
}
- 1)当前线程,正在加载Resource的集合
- 2)将正在加载的文件放入缓存中,放入失败则报异常
- 3)获取需要加载文件的字节流
- 4)将字节流封装称为InputSource
- 5)设置字符集
- 6)加载 BeanDefinition,真正加载配置文件的方法
- 7)关闭流
- 8)移除处理完Resource
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource); //1) 获取Document对象
return registerBeanDefinitions(doc, resource);// 2) 注册bean
}
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);
}
}
- 1)调用 #doLoadDocument(InputSource inputSource, Resource resource) 方法,根据 xml 文件,获取 Document 实例。
- 2)调用 #registerBeanDefinitions(Document doc, Resource resource) 方法,根据获取的 Document 实例,注册 Bean 信息。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//1)解析xml并注册bean
return getRegistry().getBeanDefinitionCount() - countBefore;
}
- 1)createBeanDefinitionDocumentReader,实例化 BeanDefinitionDocumentReader 对象
- 2)调用 createReaderContext(Resource resource) 方法,创建 XmlReaderContext 对象。
- 3)调用 BeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,解析xml并注册bean
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();//1)获取xml根节点
doRegisterBeanDefinitions(root);
}
- 1)获取xml根节点
- 2)执行注册bean
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;
this.delegate = createDelegate(getReaderContext(), root, parent);//1)创建BeanDefinitionParserDelegate
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);//2)获取激活环境
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);//3)解析xml前的处理,没有进行任何逻辑处理
parseBeanDefinitions(root, this.delegate);//4)解析xml
postProcessXml(root);//5)解析xml后的处理,没有进行任何逻辑处理
this.delegate = parent;
}
- 1)创建BeanDefinitionParserDelegate,并解析beans标签下的一些默认配置
- 2)获取自定义激活的环境
- 3)、5)这两处没有进行任何处理,开发者可以自由扩展
- 4) 真正解析xml
3.解析XML
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {// 1)
NodeList nl = root.getChildNodes();// 2)
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {//3)
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);//(4 解析
}
else {
delegate.parseCustomElement(ele);//5)
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
- 1)判断节点是否是默认namespace,http://www.springframework.org/schema/beans
- 2)获取子节点
- 3)只处理带标签的元素节点,过滤掉注释和文本节点等
- 4)解析import、alias、bean、beans等标签,并且注册到bean缓存中
- 5)解析其他标签,并且注册到bean缓存中
网友评论