java实现读取jar包中配置文件的几种方式
作者:奔跑吧人生
概述
在编程的某些情况下,我们需要读取jar包中的文件,这种情况要区别于我们平时使用类加载器读取配置文件,这个时候配置在jar包中,就能读取到,但是配置文件也可以不在jar包中,只要放在Class-Path下就行了,所以这种情况下,我更愿意把它称之为:读取Class-Path下的配置文件。而我今天描述的比较明确,就是要读取jar包中的文件。这种需求可能不多,但是我碰见了,并且发现了几种,今天全部罗列分享一下。
目前有3种:
- JarFile
- URL
- ClassLoader
定义接口
因为有好几种方式,那就直接定义个接口:
public interface JarReader { /** * 读取jar包中的文件 * @param jarPath jar包路径 * @param file jar包中的文件路径 * @return 文件内容,转换成字符串了,其它需求也可以转换成输入流。 * @throws IOException */ String readFromJar(String jarPath,String file) throws IOException; }
jar包读取器,jar包中的文件读取出来。
通过JarFile读取
JarFile是java自带的一种读取jar包的API,很多人应该用过,我就直接贴代码了。public class JarFileJarReader implements JarReader { @Override public String readFromJar(String jarPath,String file) throws IOException { JarFile jarFile=null; try { jarFile=new JarFile(jarPath); JarEntry jarEntry=jarFile.getJarEntry(file); InputStream input=jarFile.getInputStream(jarEntry); return IOUtils.toString(input,"UTF-8"); } catch (IOException e) { throw e; } finally { IOUtils.closeQuietly(jarFile); } } }
代码也比较简单,重点就是最后一定要把jarFile这个对象关闭一下,中间的输入流都可以不用关闭。
不过我在写这段代码之前,从我的个人经验上来说,JarFile好像更多是用来读取清单文件(MANIFEST.MF)的,可能是见这种情况比较多,当然它的用途肯定远不止如此。
因此我顺便写了一下读取清单文件的代码:
public void getManiFest(String jarPath) throws IOException { JarFile jarFile=null; try { jarFile=new JarFile(jarPath); Manifest manifest=jarFile.getManifest(); if (manifest!=null){ //获取Class-Path String classPaths = (String) manifest.getMainAttributes().get(new Attributes.Name("Class-Path")); if (classPaths != null && !classPaths.isEmpty()) { String[] classPathArray = classPaths.split(" "); } //获取JDK版本 String jdkVersion = (String) manifest.getMainAttributes().get(new Attributes.Name("Build-Jdk")); //还可以获取其它内容,比如Main-Class等等 } } catch (IOException e) { throw e; } finally { IOUtils.closeQuietly(jarFile); } }
通过URL读取
java自带的URL是支持读取jar中的文件的,协议是jar,表示方式的话是用"/!"把jar包和文件区分一下。代码如下:
public class URLJarReader implements JarReader { @Override public String readFromJar(String jarPath, String file) throws IOException { JarURLConnection jarURLConnection=null; try { URL fileUrl=ParseUtil.fileToEncodedURL(new File(jarPath)); URL jarUrl=new URL("jar", "", -1, fileUrl + "!/"); URL moduleUrl = new URL(jarUrl, ParseUtil.encodePath(file, false)); jarURLConnection = (JarURLConnection)moduleUrl.openConnection(); return IOUtils.toString(jarURLConnection.getInputStream(),"UTF-8"); } catch (IOException e) { throw e; } finally { if (jarURLConnection!=null){ try { jarURLConnection.getJarFile().close(); } catch (IOException ignore) { } } } } }
ParseUtil的几个方法是我在看java源码的时候看见的,用来处理一些不规则的文件路径。
我刚开始用URL的时候,就出现了一个内存泄漏的文件,读取完了以后,jar包被占用,死活不能删除,刚才开始把输入流给关闭了,也没有用。然后想到了类加载器里面有close方法,然后去看了一下,找到了上述代码中的finally块的代码。这样就可以把占用问题解决了,仔细看的话,会发现,getJarFile.close(),因此本质上还是关闭了JarFile,和上面是一样的。
通过ClassLoader
这个也是借鉴了我们平时读取配置文件的方式,借用一下ClassLoader来读取。
public class ClassLoaderJarReader implements JarReader { @Override public String readFromJar(String jarPath, String file) throws IOException{ URLClassLoader urlClassLoader=null; try { URL fileUrl=ParseUtil.fileToEncodedURL(new File(jarPath)); urlClassLoader=new URLClassLoader(new URL[]{fileUrl},null); InputStream inputStream=urlClassLoader.getResourceAsStream(file); if (inputStream==null){ throw new FileNotFoundException("not find file:"+file+" in jar:"+jarPath); }else{ return IOUtils.toString(inputStream,"UTF-8"); } } catch (IOException e) { throw e; } finally { IOUtils.closeQuietly(urlClassLoader); } } }
代码也是比较简单,最后把ClassLoader关闭一下就行了。
关于类加载器的读取方式,我很早之前就看过了,它本质上用的就是上面两种方式结合起来读取文件的。
总结
这几种方式的话,其实没有什么区别,从开发角度来说的话,比较推荐第三种,因为是java自带的功能,也是比较完善,也简单,也不容易出错,而且它内部用的就是前面两种。不过从资源消耗上面来说,我猜测,前面两种应该占优,不过我也不纠结这个,没去研究。
有时候我们或许有另外一种需求,读取jar中的jar中的文件,这个在一些场景下,会使用到。最起码spring-boot确实是用到了,很早之前我看过它的实现,它就是把URL重写了一下,支持了一下多个"/!"表达式,就能够支持这种情况了。
到此这篇关于java实现读取jar包中配置文件的几种方式的文章就介绍到这了,更多相关java 读取jar包配置文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!