一、什么是Resource
Resource 是 inputStream包装类,spring配置文件的输入流。获得了Resouce才能进行下一步的文件解析。
Resource的结构
Resource的属性和方法
从它的结构应该能发现,都是些常用的文件流操作,比如文件是否存在、文件名、文件路径等。
二、Resource的族群
Resource常见集成体系classPathResource
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
fileSystemResource
使用本地文件路径,则用该Resource进行包装
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("/"+"/code/spring-study/target/classes/applicationContext-test.xml");
urlResource
配置地址一个url的时候会启用该包装类 file:
、http:
、ftp:
三、 Resource 定位与加载
我们使用 FileSystemXmlApplicationContext 作为测试入口
//入口代码
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("/"+"/code/spring-study/target/classes/applicationContext-test.xml");
入口
//FileSystemXmlApplicationContext
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//主入口AbstractApplicationContext#refresh
refresh();
}
}
获取对应的Resource包装类
//AbstractBeanDefinitionReader#loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources)
throws BeanDefinitionStoreException {
//省略部分代码
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//省略部分代码
}
getResources()最终由DefaultResourceLoader
来实现
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//判断是否classpath开头
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 尝试使用UrlResource,若是不能识别的url协议则抛异常
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 由具体的容器去做对应的Resource包装
return getResourceByPath(location);
}
}
}
我们当前容器是FileSystemXmlApplicationContext,地址也不是url能够解析的,所以接下来就调用到了 FileSystemXmlApplicationContext.getResourceByPath返回了 FileSystemResource
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
加载Resource
//AbstractBeanDefinitionReader#loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources)
throws BeanDefinitionStoreException {
//省略部分代码
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
//省略部分代码
}
上面定位到resource则开始进行Resource的加载,由loadBeanDefinitions开始,最终由XmlBeanDefinitionReader#loadBeanDefinitions来实现,这个方法的入参是EncodedResource,是Resource的holder,多了编码的支持。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//.....................
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
//帮要加载的Resource放入threadlocal
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//.....................
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//.....................
//Resource解析后,从threadlocal中移除
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
InputStream inputStream = encodedResource.getResource().getInputStream();
这行代码,通过执行具体的FileSystemResource的getInputStream()获得文件流
//FileSystemResource
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
接着走到 XmlBeanDefinitionReader#doLoadBeanDefinitions
//代码片段
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//获取到doc
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
最后Resource输入流被解析成Document了,供下一个环境BeanDefinition的装配和注册。具体如何loadDocument的实现就不研究了,有兴趣的朋友可以自己看下
this.documentLoader.loadDocument()
四、 调用时序
本篇主要解释了Resource是什么,如何定位Resource,如何加载,最后串一下,看下整体的调用链路。
调用时序
源码版本:3.2.18.RELEASE
系列文章
spring源码探索(0)-IOC容器-架构设计
spring源码探索(1)-IOC容器-Resource
spring源码探索(2)-IOC容器-BeanDefinition加载与注册
spring源码探索(3)-IOC容器-Bean的一生
spring源码探索(4)-AOP实现原理
网友评论