Maven插件开发遇坑 - 关于ClassLoader和Reso
前言
对于 Java 开发人员来说,写一个配置文件是非常常见的,比如日志,又比如早期的 Spring,以及让人又爱又恨的 mybatis 和 hibernate 到现在也没有摆脱各种配置文件。
至少在某种层面上来说,读一个配置文件、写一个配置文件,是最基础不过、最必须掌握的事情了。
所以很多的时候,我们简单的写下:
Properties properties = new Properties();
properties.load(new FileInputStream("config.properties"));
然后,我们就开始了读取数据。
当然有些人可能小心一些,毕竟是从 classpath 读资源文件,所以他们喜欢用 ClassLoader
来读取,例如:
File configFile = this.getClassLoader().getResources("config.properties"));
Properties properties = new Properties();
properties.load(configFile);
习以为常之后,我根本没有意识到这有什么问题。
问题出现
这两天问题出现了,我们计划在写一个 Maven 插件,对于一个插件来说,一个配置文件也是少不了的,至少你需要配置一下我在哪个项目里、做什么事情、怎么做。
所以,我们写了一个配置文件,用来连接数据库,就像你们经常做的那样:
db.username = xxx
db.password = xxx
db.driver = com.mysql.jdbc.Driver
db.url = xxx
然后保存成 database.properties
存放在 resources
的根目录下,这是一个 maven 的项目,maven 已经帮我们设置了 classPath,并让 resources
文件夹成为了 resources root
类型的 path。
然后我开始了读取,在maven插件的项目里,所有的程序入口,都在一个 execute
的方法实现里。
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Props props = new Props("database.properties");
getLog().info("get configuration of ")
try {
properties.load(new FileInputStream("database.properties"));
for(Object p : properties.keySet()){
getLog().info(props.getStr(p.toString()));
}
} catch (IOException e) {
getLog().error("error ", e);
}
}
ok,一切正常,我开始了测试,就像所有 maven 的项目一样:
// 现在显然还不需要测试
mvn clean package install -DsktipTest
然后在另外一个项目的pom文件里引用在 <build>
<plugins>
<plugin>
标签下。
然后。。。
[图片上传失败...(image-fcc351-1516635482197)]
WTF???
No such file or directory...
Ok,下面来检查一下。。。
检查
检查首先需要看看文件是不是存在。
嗯,存在,没毛病。
[图片上传失败...(image-d9347e-1516635482197)]
那么接下来的问题就是,为什么没有找到。
看看刚才输出的路径:
// reverse-maven-plugin是工程的名字 v1.0是版本
file:/Users/HMH/.m2/repository/cn/hhchat/reverse-maven-plugin/v1.0/reverse-maven-plugin-v1.0.jar!/database.properties
毫无疑问,这是一个带了 file:
前缀的路径,那么应该是一个 URI,然后,指向的是一个名称为 reverse-maven-plugin-v1.0.jar
的jar包中的 database.properties
。
这大概就是问题了,当我将程序作为一个 maven
插件引用的时候,运行时应该是一个 Jar
包,所以 ClassLoader 没有办法直接定位到 database.properties
的位置。
解决方案
原来从 resources
文件夹里读取文件的需求,变成了从 jar
包的目录下读取文件。那么 maven
会把 resources
里的文件放在 jar
包的什么位置呢?
[图片上传失败...(image-19cedb-1516635482197)]
嗯,原来是在 Jar
包的根目录了,所以,现在需要看看如何从Jar包中读取文件了。
读取Jar中的文件
方法1 getResourceAsStream()
这里仍然是两种办法,第一种办法,你可以使用Stream来读取,这个变动非常小,如下所示即可:
Properties properties = new Properties();
properties.load(LoadProjectJar.class.getResourceAsStream("/database.properties"));
[图片上传失败...(image-aedb1e-1516635482197)]
这说明对于 Jar 包运行状态下的resources文件,通过 getResourceAsStream()
的方式是可以获取的文件数据的。
方法2 JarFile
使用 JarFile 来连接 jar 包中的内容是另外一种办法,可以从 jar 包中读取到文件和数据。对于 Maven 来说,这也是一种好办法,因为你可以从 Maven 运行时通过 getClassLoader()
来获取 jar 包的具体位置。
// 待续