本文是记录一下spring的ResourceLoader接口的一些实现及ResourceLoader在ApplicationContext中的作用。

实例代码

代码构造如下:

在resources/config 目录有两个配置文件
db.properties
db1=host:3306
net.properties
url=http://www.test.com

在工程援用的jar包sso.jar中也存在config目录,里边有一个sso.properties

UAS_SERVICE=http://uas.....cn/uas/...DOMAIN_NAME=defaultHTTP_SESSION_TIMEOUT=1800ssogfd.read.url=jdbc\:mysql\://10.*.*.124\:3306/test_db?characterEncoding\=utf-8ssogfd.read.username=0330ssogfd.read.password=0330jdbc.dataSource.driverClassName=com.mysql.jdbc.Driverjdbc.dataSource.initialSize=2jdbc.dataSource.maxActive=50jdbc.dataSource.maxIdle=20jdbc.dataSource.maxWait=1000

Service1.java 里是测试代码,用来测试 classpath:config/.properties 会不会将三个配置文件全副获取到。
Service1.java

public class Service1 {    public static void main(String[] args) throws IOException {        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:spring/*.xml");        Resource[] resources = context.getResources("classpath*:config/*.properties");        for (Resource resource : resources) {            Properties prop = PropertiesLoaderUtils.loadProperties(resource);            Enumeration<?>  propEum = prop.propertyNames();            Object eleKey = null;            while(propEum.hasMoreElements()){                System.out.println((eleKey=propEum.nextElement())+" : "+prop.get(eleKey));            }        }    }}

运行后果:显示jar包里的配置及工程中config目录下的配置全副能读取到。

上面咱们就到sprnig 的代码里看一下具体实现。

spring代码剖析


spring从底层资源的理论类型中形象进去的资源描述符的接口Resource接口(资源描述符),Resouce的具体实现能够代表系统文件、类门路资源、网络文件等等。spring提供了ResourceLoader接口作为加载Resouce的形象。咱们剖析就从这个接口开始。

ResourceLoader接口

/** * 加载资源的策略接口,一个Applicationcontext被要求提供相干的性能和扩大。 * DefaultResourceLoader 是一个独立与ApplicationContext之外的独自可用实现 * 这将是特定于实现的,通常由 ApplicationContext 实现提供。 */public interface ResourceLoader{    /**        返回一个指定资源地位的资源句柄。这个句柄应该是一个能够可重用的资源描述符。        必须反对全限定URLs,例如:file:c:/test.dat        必须反对classpath伪URLs,例如:classpath:test.dat        应该反对绝对文件门路,例如:WEB-INF/test.dat        */   Resource getResource(String location);   ClassLoader getClassLoader();}

ResourceLoader有一个十分重要的扩大接口ResourcePatternResover和一个重要的实现类DefaultResourceLoader 。这两个class在spring中都有十分重要的应用。

ResourcePatternResover

/** *解析一个地位模式(例如:ant-style门路模式)到Resource对象的策略接口 * PathMatchingResourcePatternResolver 是一个在ApplicationContext外能够重用的独自实现 *这个接口仅仅指定了转换方法而不是具体指定模式格局。 *这个接口反对了一个新的资源前缀"classpath*"(从classpath中找到所有的匹配资源)。 *请留神,在这种状况下,资源地位应该是没有占位符的门路(我了解应该不能是有file:  http:等占位符)。 */public interface ResourcePatternResolver extends ResourceLoader {    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";    Resource[] getResources(String locationPattern) throws IOException;}

通过正文咱们大略晓得ResourcePatternResover是解析所有合乎模式的资源的一个类,提供了相干的办法。其中CLASSPATH_ALL_URL_PREFIX 定义了咱们罕用的资源搜寻前缀。

DefaultResourceLoader

/** *ResourceLoader接口的默认实现。被ResourceEditor应用,而且作为一个根底来为AbstractApplicationContext服务。  *能够独自应用  *如果定位的值是个URL返回URLResource ,如果不是定位不是URL或者是"classpath"伪url则返回ClassPathResource */public class DefaultResourceLoader implements ResourceLoader {    @Override    public Resource getResource(String location) {        Assert.notNull(location, "Location must not be null");        for (ProtocolResolver protocolResolver : getProtocolResolvers()) {            Resource resource = protocolResolver.resolve(location, this);            if (resource != null) {                return resource;            }        }        //如果结尾是/ 返回ClassPathContextResource        if (location.startsWith("/")) {            return getResourceByPath(location);        }        else if (location.startsWith(CLASSPATH_URL_PREFIX)) {            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());        }        else {            try {                // Try to parse the location as a URL...                URL url = new URL(location);                return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));            }            catch (MalformedURLException ex) {                // No URL -> resolve as resource path.                return getResourceByPath(location);            }        }    }}

ApplicationContext

ApplicationContext 继承了接口ResourcePatternResover,间接也继承了ResourceLoader 。
AbstractApplicationContext 继承了ApplicationContext 也继承了DefaultResourceLoader。AbstractApplicationContext getResource继承自DefaultResourceLoader ,getResources实现源码如下。

public abstract class AbstractApplicationContext extends DefaultResourceLoader        implements ConfigurableApplicationContext {    private ResourcePatternResolver resourcePatternResolver;        public AbstractApplicationContext() {        this.resourcePatternResolver = getResourcePatternResolver();    }        protected ResourcePatternResolver getResourcePatternResolver() {        return new PathMatchingResourcePatternResolver(this);    }    @Override    public Resource[] getResources(String locationPattern) throws IOException {        return this.resourcePatternResolver.getResources(locationPattern);    }}

从下面代码看到,Applicaton的getResouces 是有PathMatchingResourcePatternResolver来解决的。PathMatchingResourcePatternResolver 这个重要的类终于退场了。

PathMatchingResourcePatternResolver

/** *一种 ResourcePatternResolver 实现,可能将指定的资源地位门路解析为一个或多个匹配的资源。  *源门路能够是与指标资源一对一映射的简略门路,或者能够蕴含非凡的“classpath*:”前缀和/或外部 Ant 款式的正则表达式(应用 Spring 的 AntPathMatcher 实用程序匹配) . 后者都是无效的通配符。 *没有通配符:在简略的状况下,如果指定的地位门路不以“classpath*:”前缀结尾,并且不蕴含 PathMatcher 模式,则此解析器将通过底层 ResourceLoader 上的 getResource() 调用简略地返回单个资源 *Ant格调模式:当门路地位蕴含 Ant 款式模式时。例如: *     /WEB-INF/*-context.xml *     com/mycompany/**/applicationContext.xml *     file:C:/some/path/*-context.xml *     classpath:com/mycompany/**/applicationContext.xml * 解析器遵循更简单但已定义的过程来尝试解析通配符。 它为直到最初一个非通配符段的门路生成一个资源,并从中获取一个 URL。  * 如果此 URL 不是“jar:” URL 或特定于容器的变体(例如 WebLogic 中的“zip:”、WebSphere 中的“wsjar”等),则从中获取 java.io.File 并应用 通过遍历文件系统来解析通配符。 * 对于jar URL,解析器要么从中获取java.net.JarURLConnection,要么手动解析jar URL,而后遍历jar文件的内容,以解析 通配符。 *    * 对可移植性的影响: *    如果指定的门路曾经是文件URL(显式或隐式,因为根本 ResourceLoader 是一个文件系统),那么通配符能够保障以齐全可移植的形式工作。 *  .....(后续补充) * classpath*: 前缀: *   这是通过“classpath*:”前缀检索具备雷同名称的多个类门路资源的特地反对。 例classpath*:META-INF/beans.xml 将会查找到classpath,包含classes目录或jar文件里的所有bean.xml文件。这对于自动检测每个 jar 文件中雷同地位的同名配置文件特地有用。在外部,这是通过 ClassLoader.getResources() 调用产生的,并且是齐全可移植的。 *   “classpath*:”前缀也能够与地位门路的其余部分中的 PathMatcher 模式联合应用。例如classpath*:META-INF/*-beans.xml。在这种状况下,解决策略也比较简单:在最初一个非通配符门路段上应用 ClassLoader.getResources() 调用来获取类加载器层次结构中的所有匹配资源,而后遍历每个资源,应用上述雷同的 PathMatcher 解析策略用于通配符子门路。 */public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {    @Override    public Resource[] getResources(String locationPattern) throws IOException {        Assert.notNull(locationPattern, "Location pattern must not be null");        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {            // a class path resource (multiple resources for same name possible)            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 {            // Generally only look for a pattern after a prefix here,            // and on Tomcat only after the "*/" separator for its "war:" protocol.            int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :                    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)};            }        }    }protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {        //最初一个非通配符段的门路,例如:classpath*:/config/*-bean.xml,rootDirPath是classpath*:/config        String rootDirPath = determineRootDir(locationPattern);        //去除rootDitPath的门路,可能蕴含匹配模式,也可能不蕴含        String subPattern = locationPattern.substring(rootDirPath.length());        //从classPah(classes 和jar)获取rootDirPath,        Resource[] rootDirResources = getResources(rootDirPath);        Set<Resource> result = new LinkedHashSet<>(16);        //遍历获取到的rootDitPath的所有资源        for (Resource rootDirResource : rootDirResources) {            rootDirResource = resolveRootDirResource(rootDirResource);            URL rootDirUrl = rootDirResource.getURL();            if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {                URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);                if (resolvedUrl != null) {                    rootDirUrl = resolvedUrl;                }                rootDirResource = new UrlResource(rootDirUrl);            }            if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {                result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));            }            else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {                result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));            }            else {                result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));            }        }        if (logger.isTraceEnabled()) {            logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);        }        return result.toArray(new Resource[0]);}

通过各个类的代码咱们晓得,在applicationContext中应用的还是PathMatchingResourcePatternResolver还获取相干资源,这个类在很多中央都在应用。

warning

PathMatchingResourcePatternResolver的正文有几个warning,独自拿进去记录一下。

warning1

当与 Ant 款式模式联合应用时,“classpath:”只能在模式开始前至多在一个根目录下牢靠地工作,除非理论的指标文件驻留在文件系统中(classes中).这就象征这像classpath:*.xml 不会从jar文件中检索xml文件,而只是从扩大目录中获取。这源于 JDK 的 ClassLoader.getResources() 办法的限度,该办法仅返回传入的空字符串的文件系统地位。

warning2

如果要搜寻的根包(rootdir)在多个类门路地位中可用,则不能保障具备“classpath:”资源的 Ant 款式模式找到匹配的资源。例如:com/mycompany/package1/service-context.xml 可能在project中只有一个地位,过后当应用classpath:com/mycompany/*/service-context.xml 来尝试解析它,则解析器将解决 getResource("com/company") 返回的(第一个)URL.所以如果此基础包节点存在于多个类加载器地位,则理论的最终资源可能不在上面。 "classpath:"