最近做一个项目需要用到插件包的动态加载功能,随意百度了一下jar包动态加载的一些知识,需求上需要从jar中读取一部分文件,看了一下网络上用的比较多的这两种方法。
加载jar:
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{url});
读取jar包中的配置:
方法一:
InputStream is = loader.getResourceAsStream("xxxx");
方法二:
URL url = loader.findResource("xxxx");
paramurlc = url.openConnection();
InputStream is = paramurlc.getInputStream();
遇到的问题:
方法一
读取基本上没有抱什么错误,不过这个方法会先默认先从主目录中查找是否又相关的配置文件,也就是说如果主服务中已经有重名的配置文件就会优先读取主服务中的配置。其次是会去查找已经加载过的插件包中的配置,如果也没有才会从当前加载的插件包中获取。
所以如果几个插件包重名的配置文件,这个方法最终只会读取其中的一个。
方法二
相对于方法一,方法二要精准很多,它可以精确的指定读取哪个插件包的哪个配置文件。不过当重复加载同一个jar包时,方法二就会找不到配置文件而报错。这点方法一倒是不会。
方法二问题的解决方案:
在调试方法二的时候发现了一个问题,就是方法一在后台关闭掉所有相关的线程和定时器之后这个jar包是可以删除的(windows环境)。但是方法二在相同的情况下确实提示jar被占用,这就开始 哦莫昔洛依 了起来。
↓↓↓↓↓↓ 整了半天之后发现问题出在了这里 ↓↓↓↓↓↓
paramurlc = url.openConnection();
这部句代码上,虽然每次执行完都会对流进行关闭,但是这个openConnection()貌似重来没有关闭过的样子。又查了好一会资料找到了方法:
((JarURLConnection) paramurlc).getJarFile().close();
加上这段代码之后删除jar包就没有问题了,同时方法二也可以正常更新不会出现找不到配置文件的问题。
完整代码:
File jarFile = new File(fileName);
if (jarFile.exists()) {
URLConnection urlc = null;
InputStream is = null;
try {
//获取jar文件的url路径
URL url = file.toURI().toURL();
// 获取系统类加载器
URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
//jar路径加入到系统url路径里
URL pzurl = classLoader.findResource("config.properties");
// InputStream is = loader.getResourceAsStream("config.properties");
urlc = pzurl.openConnection();
is = urlc.getInputStream();
if (is != null) {
//TODO 读取文件流后操作......
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(is != null) {
is.close();
}
if (urlc != null) {
try {
((JarURLConnection) urlc).getJarFile().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}