关于tomcat下spring无法加载依赖jar中properties文件的原因分析
2015-01-07 15:12
204 查看
我们经常把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方法里面有以下内容
如果我们用classpath*:config-*.properties来查找资源,会进入findPathMatchingResources方法,我们看看这个方法前几行怎么写的
throws IOException 这个方法里面,(看代码)
,接下来并不会再调用findPathMatchingResources方法而是调用findAllClassPathResources这个方法,参数会把”classpath*:”这个subString掉,这个时候神奇的地方来了,我们看看findAllClassPathResources这里面有什么神奇的,上代码:
代码很简单,关键就在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!
这个方法实际是WebappClassloader直接继承了ClassLoad过来的,parentClassLoad实际查找的的时tomcat容器的classpath关键在于findResources(name)这个调用,这个调用webappCLassPath有自己的实现,继续look:
我们可以看到这个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是如何不让这种现象出现的.
首先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是如何不让这种现象出现的.
相关文章推荐
- 关于springMVC deploy时 启动tomcat出现找不到spring的xml配置文件的原因
- Spring 依赖注入 + 装配Bean + 注解 + profile + 加载properties文件
- 关于Spring+MyBatis的MapperScannerConfigurer无法加载place-holder属性问题分析
- 关于加载Spring加载外部文件属性.properties的问题
- SpringMVC异常总结:启动tomcat时出错,无法正取加载spring配置文件
- eclipse 打出的jar中包含的spring文件,无法被加载!
- Spring配置文件打包到jar中无法加载问题之解决方案
- 可以从Jar外部加载JDBC.properties的Spring-mybatis配置文件
- 可以从Jar外部加载JDBC.properties的Spring-mybatis配置文件
- eclipse中启动spring-mvc项目时无法加载静态文件的问题分析
- 关于java无法加载到rt.jar文件
- Spring 加载 *.properties 文件的源码分析
- spring+mybatis 注入properties文件中属性失败导致datasource加载失败的原因和解决
- Maven打包可执行的jar文件(包含所有依赖和不存在spring无法解析schema)
- tomcat无法正常启动的原因分析
- ArrayList 与HashSet的比较,及应用反射读取properties配置文件中的数据进行实例化再调用,以及类加载器的使用;还有HashCode的分析,及导致内存泄露,内存溢出的原因之一
- 加载依赖的jar包在命令行编译和运行java文件
- tomcat无法正常启动的原因分析
- 加载.properties文件内容到内存----tomcat服务启动与停止的监听(使用ServletContextListener)
- 关于设置sql server 2008服务器属性时出现的无法加载xplog70.dll文件的问题