我的spring笔记系列2----资源文件加载
Spring将资源文件定义为Resource接口,一个配置源,一个配置信息
其中最重要的方法是InputStream getInputStream();
这个接口的作用其实就是定位到具体的配置,能够用流的方式把配置读入。
常用的几个Resource实现:
ClassPathResource—调用class或classLoader的getResourceAsStream()方法
FileSystemResource----返回FileInputStream
ServletContextResource----调用ServletContext.getResourceAsStream
UrlResource----url.openConnection().getInputStream()返回url的流
ByteArrayResource----将byte数组按ByteArrayInputStream返回
InputStreamResource----封装一个InputStream
DescriptiveResource----拥有一个资源的描述,但不直接指向任何资源
载入:
配置信息载入是靠BeanDefinitionReader来完成的,
完成主要功能的4个方法:
1、从Resource里解析出单个bean的配置,然后生成BeanDefinition对象,然后把bean放入map中,完成这个事情的主要方法就是loadBeanDefinitions(Resource resource)。
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
2、这个方法是调用loadBeanDefinitions(Resource resource)的
int loadBeanDefinitions(Resource[] resources) throws BeanDefinitionStoreException;
3、这个方法需要将String location转换成Resource resource之后再调用loadBeanDefinitions(Resource resource)方法
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
4、这个方法是调用loadBeanDefinitions(String location)的
int loadBeanDefinitions(String[] locations) throws BeanDefinitionStoreException;
另外两个辅助方法:
1、BeanDefinitionRegistry getRegistry();
获取BeanDefinition对象要注册的那个容器,重要实现类DefaultListableBeanFactory
2、ResourceLoader getResourceLoader();
ResourceLoader主要工作是将String location转换为Resource
主要方法是: Resource getResource(String location);
以及其子类ResourcePatternResolver extends ResourceLoader中定义的
Resource[] getResources(String locationPattern) throws IOException;
从一个locationPattern中获取多个Resource
Resource getResource(String location)主要实现在DefaultResourceLoader中:
这个方法从location中获取一个Resource,主要实现为:
if (location.startsWith(“classpath:”)) {
返回ClassPathResource
} else {
try {
//如果是URL则返回UrlResource
URL url = new URL(location);
return new UrlResource(url);
}catch (MalformedURLException ex) {
//如果不是URL则交给getResourceByPath方法来完成
return getResourceByPath(location);
}
}
实现了getResourceByPath方法的主要类有:
DefaultResourceLoader----直接返回ClassPathContextResource
AbstractRefreshableWebApplicationContext
----直接返回ServletContextResource
FileSystemXmlApplicationContext----直接返回FileSystemResource
GenericWebApplicationContext----直接返回ServletContextResource
StaticWebApplicationContext----直接返回ServletContextResource
FileSystemResourceLoader----直接返回FileSystemContextResource
ServletContextResourceLoader----直接返回ServletContextResource
也就是ResourceLoader.getResource可能返回的对象有ClassPathResource、UrlResource、ClassPathContextResource、ServletContextResource、FileSystemResource、FileSystemContextResource
Resource[] getResources(String locationPattern) 主要实现在PathMatchingResourcePatternResolver中:
if (locationPattern.startsWith(“classpath*:”)) {
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
Spring资源定位问题:
可以有*、?两种通配符,
classpath开头表示只要找到一个即返回,可以支持通配符,通配符的实现方式是先将通配符所在那一级目录全部找出来,再去匹配
他的实现方式是会根据通配符递归找到符合规范的配置文件,但是因为调用的是classloader 的URL getResource(String name)方法,每次getResource时都只要找到一个就返回
例如:com/qie/spring/test/xml/a?.xml的查找过程是:
1、先找到com/qie/spring/test/xml/目录
2、然后把该目录的全部文件和文件夹列表出来
3、一个一个的匹配a?.xml
对jar包里的:
1、先找到com/qie/spring/test/xml/对应的jar路径,然后生成JarFile
2、列出所有的JarEntry
3、一个一个匹配com/qie/spring/test/xml/a?.xml
注意:jar包的方式,生成jar的时候如果没有把目录信息打进去,com/qie/spring/test/xml/对应的jar是找不到的
这两个方式可以同时存在
classpath* 调用的是classloader的Enumeration<URL> getResources(String name)方法,能将所有符合路径的文件都找出来
FileSystemXmlApplicationContext和ClassPathXmlApplicationContext支持${}引用系统变量,包括java的系统变量像user.home等,以及操作系统的环境变量JAVA_HOME、CLASSPATH等
FileSystemXmlApplicationContext 中用classpath(classpath*):com/qie/spring/test/xml/aa.xml定义配置文件路径,依然会用classpath的方式加载,
因为这两个ApplicationContext都使用PathMatchingResourcePatternResolver类中的Resource[] getResources(String locationPattern)来查找资源,classpath*的支持是在该方法中的,对不是classpath*开发的文件加载都用DefaultResourceLoader类中的Resource getResource(String location)方法加载,这个方法的实现是
如果classpath开头,直接返回ClassPathResource
否则以URL方式尝试,也就是new URL,如果能成功则返回UrlResource
否则以Resource getResourceByPath(String path)方法返回,这个方法在DefaultResourceLoader中的默认实现是用ClassPathContextResource的
在FileSystemXmlApplicationContext中重载了Resource getResourceByPath(String path)方法,返回FileSystemResource
在XmlWebApplicationContext中通过继承AbstractRefreshableWebApplicationContext重载Resource getResourceByPath(String path)方法,返回ServletContextResource