使用jlink工具模块化Java应用,结合Docker优化容器镜像(上)
使用jlink工具模块化Java应用,结合Docker优化容器镜像(中)
依赖统一管理
当项目中的依赖越来越多时,对模块的管理会越发的复杂,容易出错。这里使用maven-dependency-plugin插件对依赖做统一的管理,减少工作量,避免不必要的模块管理工作。注意:这个插件需要放在第一个插件的位置,避免将其它插件生成的依赖包覆盖掉。
- 使用maven-dependency-plugin,将项目所有的依赖放在同一个目录,实现模块的目录路径统一。通过指定outputDirectory,将项目用到的所有依赖都复制到该目录下面。
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/modules
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
- 在modulePath中使用<path>${project.build.directory}/modules</path>,将该目录下的所有依赖包都作为模块导入项目。因为项目自身的模块不在该目录下,所以要单独导入,即第一个<path>节点。
<configuration>
<modulePath>
<path>
${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}
</path>
<path>
${project.build.directory}/modules
</path> <!-- 添加的代码 -->
</modulePath>
</configuration>
添加自身是模块的依赖
接下来改进程序和代码。你们可能知道,在代码中直接使用System.out.println()是不明智的,因为它们无法被配置,因此它们只能达到调试的目的,并且即使发布产品时,它们依然会输出到控制台。让我们使用一个适当的日志框架来代替这个日志声明。
我将使用已经被模块化的slf4j日志框架[https://www.slf4j.org/]。这个选择需要添加两个依赖:API(slf4j-api)和单一实现(slf4j-simple)模块。
在POM文件中添加如下依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.8.0-beta2</version>
</dependency>
更新项目的module-info.java:
module ch.frankel.jlink {
requires org.slf4j.simple;
requires org.slf4j;
exports ch.frankel.blog.jlink;
}
这里使用的依赖都是被模块化的,即依赖包的根目录下是自带module-info.java文件的。如果我们引入的是一个未被模块化的依赖包该怎么办呢?
添加非模块化的依赖
这里我们已javax.jms-1.1消息服务依赖为例,这个1.1版本肯定是不具备模块化的,但是在jlink中是不允许使用非模块化的依赖的,所以JDK中提供了一个jdeps工具对非模块化的依赖处理成模块化的。但是我们可以继续通过moditect-maven-plugin插件简单并统一的添加非模块化依赖,代码如下:
- 添加jms-1.1依赖:
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
</dependency>
- 在moditect-maven-plugin插件节点中添加add-module-info执行节点,对指定的非模块化依赖包创建并添加module-info.class文件,生成新的依赖包,从而实现对该依赖包的模块化。注意:需要将生成的依赖包路径指向指定的路径,并设置可对包文件进行覆盖写入。如果需要添加多个非模块化依赖,在<modules>节点中按格式添加多个<module>节点即可。
<execution>
<id>add-module-info</id>
<phase>package</phase>
<goals>
<goal>add-module-info</goal>
</goals>
<configuration>
<modules>
<module>
<artifact>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
</artifact>
<moduleInfo>
<name>jms</name>
</moduleInfo>
</module>
</modules>
<outputDirectory>
${project.build.directory}/modules
</outputDirectory>
<overwriteExistingFiles>true</overwriteExistingFiles>
</configuration>
</execution>
至此,你已经完全掌握了对jlink这个工具的使用姿势。一个项目经过jlink工具的模块化,就能实现在未安装JDK9或更高版本的计算机上运行你的Java应用了,因为我们生成的是一个可单独运行的镜像。当然你还可以通过编写一个vbs文件再使用AutoIt3这个工具,生成一个exe执行文件,具体使用视项目情况而定。