码迷,mamicode.com
首页 > 编程语言 > 详细

关于tomcat下spring无法加载依赖jar中properties文件的原因分析

时间:2015-01-07 16:59:36      阅读:163      评论:0      收藏:0      [点我收藏+]

标签:spring   properties   tomcat   classpath   

我们经常把spring需要加载的properties文件放在java/resources下面,这样存放的问题导致properties在打包后就在jar的根目录下,所以我们的spring的配置路径就是classpath*:xxx.properties,但是这样的jar我们在被其他项目引用的时候会发现properties文件老是无法加载,就这个问题从spring的源码来找找为什么会这样.

首先properties是当做一个resource来加载的,实现加载的是org.springframework.core.io.ResourceLoader接口的一个实现,其中org.springframework.core.io.support.PathMatchingResourcePatternResolver用于解析classpath*:为前缀的资源定义.

PathMatchingResourcePatternResolver的getResources方法里面有以下内容

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

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()));
}
}

如果我们用classpath*:config-*.properties来查找资源,会进入findPathMatchingResources方法,我们看看这个方法前几行怎么写的

protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
这里经过determineRootDir(locationPattern)产生的rootDirPath会变成classpath*:然后就会调用到public Resource[] getResources(StringlocationPattern) throws IOException 这个方法里面,(看代码)

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()));
}
}

,接下来并不会再调用findPathMatchingResources方法而是调用findAllClassPathResources这个方法,参数会把”classpath*:”这个subString掉,这个时候神奇的地方来了,我们看看findAllClassPathResources这里面有什么神奇的,上代码:

protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
Enumeration<URL> resourceUrls = getClassLoader().getResources(path);
Set<Resource> result = new LinkedHashSet<Resource>(16);
while (resourceUrls.hasMoreElements()) {
URL url = resourceUrls.nextElement();
result.add(convertClassLoaderURL(url));
}
return result.toArray(new Resource[result.size()]);
}

代码很简单,关键就在Enumeration<URL> resourceUrls = getClassLoader().getResources(path);(注意这个时候path=“”)这一行,这里的classloader决定了我们能获取的资源,如果在tomcat下面.这个classloader是Tomcat的WebappClassloader,这个时候我们查找资源的path是一个空的字符串.这个class返回了两个URl,一个是${webapps}/WEB-INF/classes,还有一个就是${tomcat-home}/lib,奇怪了,居然${webapps}/WEB-INF/lib并不在返回的URL中,有点神奇了,let’s look!

public Enumeration<URL> getResources(String name) throws IOException {
        Enumeration[] tmp = new Enumeration[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);

        return new CompoundEnumeration<>(tmp);
 }

这个方法实际是WebappClassloader直接继承了ClassLoad过来的,parentClassLoad实际查找的的时tomcat容器的classpath关键在于findResources(name)这个调用,这个调用webappCLassPath有自己的实现,继续look:

@Override
    public Enumeration<URL> findResources(String name) throws IOException {

        …..这里省略N多代码
        // Looking at the JAR files
        synchronized (jarFiles) {
            if (openJARs()) {
                for (i = 0; i < jarFilesLength; i++) {
                    JarEntry jarEntry = jarFiles[i].getJarEntry(name);
                    if (jarEntry != null) {
                        try {
                            String jarFakeUrl = getURI(jarRealFiles[i]).toString();
                            jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
                            result.add(new URL(jarFakeUrl));
                        } catch (MalformedURLException e) {
                            // Ignore
                        }
                    }
                }
            }
        }

      ……这里也省略掉N多代码
        return Collections.enumeration(result);
    }

我们可以看到这个for循环里面获取JarEntry这一句,JarEntry jarEntry = jarFiles[i].getJarEntry(name);这个name其实是一个””字符串,所以返回的jarEntry就是null,这样所有WEB-INF/lib下jar包里面的配置文件全部都不会进入下一步的匹配过程中.

所以这个问题的根源在于classpath*:这种配置,在tomecat下并不适用于文件放在根目录下匹配加载的情况,所以想加载lib/xxx.jar包里面的porpertis文件的话,需要将porperties文件放在一个包路径下,我是放在config这个包路径下解决,可以参考解决一下.


特别说明jetty下没有这个情况,所以问题都是在tomcat下出现的,以后抽时间再看看jetty的classpath是如何不让这种现象出现的.

关于tomcat下spring无法加载依赖jar中properties文件的原因分析

标签:spring   properties   tomcat   classpath   

原文地址:http://blog.csdn.net/coffee_hc/article/details/42493351

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!