`

Java载入Jar内资源问题的探究

    博客分类:
  • java
 
阅读更多

转自:http://www.blogjava.net/cenwenchu/archive/2008/05/28/203560.html

 

工作忙,有些许时间没有更新Blog了,这次在开发监控模块的时候遇到了这个问题,整个问题定位过程真是走了不少路,所以觉得有必要记录下来分享一下。在我看来很多时候结果也许就很简单一个原因,但是开发人员却要探究很久,也许在找到了其他可实现业务逻辑方法的情况下,就会放弃寻找原因,这期间我也是一样。

问题初现:

       在服务集成平台中需要新增一块写入数据库的逻辑,因此考虑最简便就是弄个SpringBeanFactory来搞定这一切,谁知道,问题就这么出现了。很简单,通过SpringClassPathXmlApplicationContext来构建BeanFactory,下面的语句大家应该很熟悉:

ctx = new ClassPathXmlApplicationContext("/spring/sip-*.xml");

       通过通配符来载入ClassPath下的所有的符合规则的spring配置文件。然后在Eclipse中作完单元测试和集成测试,一切正常。然后用我们内部的打包部署,而这些配置文件都被打在Jar中作为lib库依赖。结果启动以后,在分析完日志需要写入到数据库的时候出现异常:

Could not resolve bean definition resource pattern [/spring/sip-*.xml]; nested exception is java.io.FileNotFoundException: class path resource [spring/] cannot be resolved to URL because it does not exist

就提示来说,就是没有找到spring这个目录,也就是在ClassPath下面就没有找到资源。

第一次试图解决问题:

以前调整过Jboss关于ClassLoader的配置,即自上而下搜索还是自下而上搜索,以及是否采用Web容器的ClassLoader,开始怀疑是否是这种修改造成的问题。修改了没有问题,然后就设置断点跟踪SpringClassPathXmlApplicationContext的构造过程,发现Spring在分析此类通配类型的过程中,首先将前面的文件目录和后面的具体通配文件分开,先定位文件目录资源,也就是在定位文件目录资源的过程中,找不到spring目录,而出现了那个异常。看了代码中也有对Jar的处理,但是在处理之前就出现了问题。

自己做了尝试,将spring目录和其内容解压到WebClasses目录下运行正常,或者解压到war下面也是正常的,这些地方其实都是ClassPath可以找到的,但是lib目录下的jar也应该是可以找到的。在仔细跟踪了代码中最后载入这些资源的ClassLoader内的数据,所有的Jar都是包含在内的。

由于工作太多,因此将原有的打包模式作了修改,每次打包将这部分配置解压到war下面,这样就找到了可解决方案了,因此细致的缘由也就没有再去追究。(如果不是后面再次遇到,这个问题就会在此了结)

问题再现:

       监控模块中需要新增一块写入数据库的逻辑,在单元测试和集成测试通过的情况下出现了问题,由于此次是普通的J2SE的应用,所有的配置和依赖都打入在了Jar中,所以问题和前次一样。

       这次决定花一些时间好好找到问题所在,首先觉的Spring的资源载入应该不会不支持从Jar中载入,这是最基本的功能,因此再次打开了Spring的源码。

问题二次定位:

先看看ClassPathXmlApplicationContext的类图结构:




    关键方法就是getResource方法,ClassPathXmlApplicationContext的资源定位就是采用了DefaultResourceLoadergetResource方法。内部也没有做太多的工作,其实就是如下的代码:

try

{

                      // Try to parse the location as a URL...

                      URL url = new URL(location);

                      returnnew UrlResource(url);

           }

           catch (MalformedURLException ex)

{

                 // No URL -> resolve as resource path.

                 return getResourceByPath(location);

      }

上面的代码都是标准的j2se的代码.作为URL通过字符串来构造,通常需要能够首先获得URL的资源全路径,而在当前情况下发现到获取资源的时候location还是保持了spring/的状态,而没有被替换成为所在jar的资源全路径,那么就先作以下测试:

    新建简单的项目,然后在项目中加入包含spring配置的jar,然后作单元测试,测试代码如下:

URL url = Thread.currentThread().getClass().getResource("/spring/");

    未获取到URL,出现异常。

URL url = Thread.currentThread().getClass().getResource("/spring/sip-analyzer-dataSource.xml");

       正常获取到了URL

由此看来应该是在获取Jar中的目录资源路径的时候出现问题导致后续载入出现问题,尝试直接传入具体的文件名:

ctx = new ClassPathXmlApplicationContext("/spring/sip-analyzer-dataSource.xml");

发现还是出现问题,在new URL的时候传入的是没有翻译过的文件名,考虑在传入的过程中就直接替换成为资源路径,因此写了一个简单的方法:

publicstatic String[] getRealClassPath(String[] locationfile)

      {

           String[] result = locationfile;

                 for(int i = 0 ; i < locationfile.length; i++)

                 {

                      try

                      {

                            URL url = Thread.currentThread().getClass().getResource(locationfile[i]);

                            String file = url.getFile();

                            if (file.indexOf(".jar!") > 0)

                                  result[i] = newStringBuffer("jar:").append(file.substring(0,file.indexOf(".jar!")+".jar!".length()))

                                             .append(locationfile[i]).toString();

                      }

                      catch(Exception ex)

                      {}

                 }

          

           returnresult;

}

在将构造工厂类修改为:

ctx = new ClassPathXmlApplicationContext(BaseUtil.getRealClassPath(new String[]{"/spring/sip-analyzer-dataSource.xml"}));

运行测试,正常启动,这也就是又变成最原始的文件罗列的模式。问题虽然找到了解决方案,但是始终觉得很别扭,同时对于无法在Jar中载入配置资源的情况我一直都还是觉得应该不是Spring的问题。

峰回路转:

晚上到家还是有点不死心,就直接建了个项目作单元测试,然后将一个自己建立的Jar加入到Classpath下面,作单元测试,结果大吃一惊。

URL url = Thread.currentThread().getClass().getResource("/test/");

URL url = Thread.currentThread().getClass().getResource("/test/test.txt");

都正常获取到了资源,这完全推翻了我早先认为在Jar中无法获得目录作为资源的问题。然后把公司里面的项目重新打包然后加入到ClassPath下,验证spring的目录,出错,目录无法获取,此时我确定看来应该不是应用的问题,而是环境问题。检查了两个Jar,看似没有什么区别,将公司项目的Jar中的spring目录拷贝到测试的jar中,然后作测试,可以找到目录。那么问题完全定位到了Jar本身。通过RAR的压缩工具看了一下两个Jar的信息,除了显示所谓的压缩平台不同(一个是DOS,一个是Unix)其他没有任何区别。然后自己用RAR打了一个Jar以及在linux下打了一个Jar做了测试,两个Jar内的目录都是正常可以被获取。

无意中我换了一下需要获取的目录名称,也就是说在公司项目中有多个目录在jar中,这次换成为ibatis目录,正常获取,看来不是Jar的格式。回想了一下,公司的打包工具是自己人写的,其中提供了一个特性,如果一个项目内部的一些配置信息是需要让调用它的第三方在编译期配置,那么可以通过在第三方项目构建的过程中,动态的生成配置文件然后植入到被依赖的jar中。而spring这个目录中由于那些数据库的配置都是需要动态配置的,因此spring的那个目录是后期被写入的,而ibatis是早先就固化在项目中的。

由于我们的JarMETA-INF中都有INDEX.LIST文件,过去遇到过在JAR中自己手工放入一些文件由于没有修改INDEX.LIST而导致虽然文件已经存在但是不会被发现,于是打开公司项目中的Jar,果然INDEX.LIST中只有ibatis,而没有spring,看来是我的同事在写入的时候没有将INDEX.LIST更新。立刻将INDEX.LIST作了更新,测试spring目录,结果依然出错。看来这还不是问题的根本。

立刻问了我们开发打包工具的同事,向他们要写入Jar的代码,对方的回答是就是采用简单的JarOutputStream来写入,没有什么特殊的。那我就开始怀疑是否是因为采用这种方式写入到Jar中的目录在被资源定位的时候会出现问题。于是写了下面的代码:

JarOutputStream jos;

           try

           {

                 jos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(file)));

                 String f = "spring/sip-analyzer-dataSource.xml";

                 File source = new File(f);

                 JarEntry je = new JarEntry(f);

                 jos.putNextEntry(je);

                 BufferedInputStream bis = new BufferedInputStream(newFileInputStream("D:/work/sip3/analyzer/src/conf.test/spring/sip-analyzer-dataSource.xml"));

                 int i = 0;

                 while ((i=bis.read())!=-1)

                 {

                      jos.write(i);

                 }

                 bis.close();

                 jos.closeEntry();

                 jos.close();

catch  ...

结果创建出来的Jar中的spring目录无法被资源定位,同样在这个Jar中直接拖入一个目录test,然后刷新测试,test目录可以被定位。

后续

       就到了这个阶段来看如果以上面这种方式写入,对于目录资源定位的却存在问题。这个问题还没有最终的肯定的结论,在我现在所有的试验来看,不论是否有INDEX.LIST,或者INDEX.LIST,如果用程序写入到Jar中,目录作为资源定位都会出现问题(起码是上面那种普通写入方式)。

       这种情况可能是由于这种写法还有一些其他需要配置的,例如写入到META-INF/INDEX.LIST中,或者就是J2SE现在API存在的一个问题。不过不管是什么问题,起码值得引起重视,特别是现在类似于Spring框架载入Jar目录下的配置。

分享到:
评论

相关推荐

    在可执行jar中载入第三方jar的几个解决方法

    在Java开发中,将项目打包成可执行JAR(Java Archive)文件是常见的发布方式,尤其是在需要集成第三方库的情况下。然而,当从可执行JAR加载第三方JAR中的类时,常常会遇到“ClassNotFoundException”。这个问题通常...

    runqian_report4.jar、runqianReport4Applet.jar

    此JAR包包含的类和资源使得用户无需安装额外软件,即可在Web浏览器上进行报表操作,提升了用户体验。 在实际开发中,可能会遇到与原版润乾报表JAR包不兼容或者存在错误的情况,这可能会影响到报表的打印、导出等...

    java 载入dll之后无法切换输入法测试工程(My Eclipse)

    总的来说,解决"java 载入dll之后无法切换输入法"的问题需要对Java、JNI、DLL和Windows操作系统有深入的理解,同时需要具备良好的调试技巧。通过仔细分析代码、调试和日志记录,通常能找到问题的根源并提出解决方案...

    Java动态生成代码并编译载入.pdf

    Java动态生成代码并编译载入是一个高级的编程技术,主要用于那些需要在应用程序运行时根据某些模板和数据动态地生成、编译和加载Java代码的场景。这种技术在需要高度定制化处理或是运行时参数化生成业务逻辑的系统中...

    解决网上应用厅部署在was上jar包冲突问题

    通过调整类加载顺序为“类已载入并且先使用本地类装入器(父类最后)”,可以确保应用优先使用自身WAR包中的JAR包,从而避免版本冲突问题。 #### 扩展阅读 - **类加载机制**:深入了解Java的类加载机制对于理解和...

    J2ME模拟器 KEmulator Lite V0.9.8 中文版含QQ2007.jar.rar

    J2ME(Java 2 Micro Edition)是Java平台的一个子集,专门设计用于资源有限的设备,如早期的移动电话。KEmulator Lite是一个流行的J2ME模拟器,它允许开发者和用户在个人电脑上测试和运行J2ME应用程序。 ### ...

    AES256加密工具类,及其所必须的jar包

    在这个资源中,`AES256Util.java` 是一个实现了AES256加密和解密功能的Java类。通常,这个工具类会包含以下方法: 1. `encrypt()`:用于将明文数据加密为密文。它接受明文字符串和密钥作为输入,返回加密后的字节...

    java监控.rar

    java -jar java_monitor-0.0.2-SNAPSHOT.jar --server.prot=8888 启动成功后访问默认端口8888 1.自定义端口 在执行jar包时追加参数 --server.port=9999 2.自定义监控周期 默认监控频率为60秒,并且只记录当天产生...

    超强手机java模拟器KEmulator Lite中文破解版(模拟jar程序)

    3、点击菜单【文件】-【载入 jar...】找到我们的手机JAVA程序(可从网上下载),一般为jar格式,然后耐心等一会就可以看到KEmulator Lite运行手机程序了。 4、点击菜单【视图】-【模拟键盘】可打开模拟键盘,左软...

    Java动态生成代码并编译载入

    Java动态生成代码并编译载入是Java编程中的一项高级技术,它允许程序在运行时创建新的类或接口,然后即时编译并加载到当前的Java虚拟机(JVM)中。这种技术主要依赖于Java的反射API和Java的编译器API(javac)或者更...

    spring jar 包详解

    - **依赖关系**:依赖于 `spring-core.jar`、`spring-beans.jar`、`spring-aop.jar`、`spring-dao.jar`、`spring-jdbc.jar`、`spring-orm.jar`、`spring-web.jar` 和 `spring-webmvc.jar`。 ##### 7. spring-jdbc....

    rhino-1.7.14.jar下载

    rhino-1.7.14.jar下载

    易语言载入易语言载入

    易语言载入易语言载入易语言载入易语言载入易语言载入易语言载入易语言载入易语言载入易语言载入易语言载入易语言载入

    Java技术----实现JAVA的动态类载入机制

    在Java编程语言中,动态类加载机制是一种强大的特性,它允许程序在运行时加载、实例化和执行未在编译时硬编码的类。这种能力是通过Java的反射API实现的,它为开发者提供了深入洞察和操作Java对象的能力。本文将深入...

    openblas-0.3.19-1.5.7-API文档-中文版.zip

    标签:bytedeco、openblas、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心...

    aspectjweaver.jar

    aspectjweaver.jar是java Spring aop 编程必导入的jar包之一

    dex2jar一键反编译apk

    打开JD-GUI,然后载入由dex2jar生成的.jar文件,就可以看到类似于源代码的表示形式。 5. **注意事项**:使用dex2jar进行反编译可能会涉及法律问题,因为这可能侵犯了软件的版权。只有在你拥有合法权限或者进行个人...

    jboss-modules.jar

    不同于Java传统的使用单个类加载器载入classpath中的所有JAR文件,每一个库(library,可以理解为完成某一个功能的一系列jar的组合)成为一个module,该module仅链接其依赖的其他module,而不再依赖其它任何资源。...

    在Java_3D中载入外部3D模型文件.pdf

    在Java_3D中载入外部3D模型文件.pdf

Global site tag (gtag.js) - Google Analytics