我正在尝试以编程方式创建一个fat-jar。到目前为止,我成功创建了一个包含我的代码的 Jar 文件。问题是,如果我运行这个,我会收到一个异常,说
Cannot load user class: org.company.bla.bla
。
这是我的代码
private static void createJar() throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream("events.jar"), manifest);
Path root = Paths.get(".").normalize().toAbsolutePath();
System.out.println(root.toString());
Files.walk(root).forEach(f -> add(f.toFile(), target));
target.close();
}
private static void add(File source, JarOutputStream target)
{
BufferedInputStream in = null;
try {
if (source.isDirectory()) {
String name = source.getPath().replace("\\", "/");
if (!name.isEmpty()) {
if (!name.endsWith("/"))
name += "/";
JarEntry entry = new JarEntry(name);
entry.setTime(source.lastModified());
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile: source.listFiles())
add(nestedFile, target);
return;
}
JarEntry entry = new JarEntry(source.getPath().replace("\\", "/"));
entry.setTime(source.lastModified());
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(source));
byte[] buffer = new byte[1024];
while (true) {
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我怎样才能将依赖项添加到这个 Jar 中?或者依赖项所在的路径在哪里,以便我可以包含它们?
创建 Uber / Shadow JAR 意味着 Maven 或 Gradle 也用于处理所有库依赖项。
如果是这样,一种解决方案是从 Java 运行 Maven 或 Gradle 构建命令行参数(使用
Runtime.exec()
命令)。
以编程方式执行此操作的用例是作为 Java 集成测试的一部分,其中需要 Uber Jar(有关示例,请参阅使用测试容器测试自定义 Kafka Connect SMT)。
以下解决方案同时支持 Gradle / Maven。
确保
pom.xml
包含 Maven Shade Plugin 插件,例如
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>helloworld</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jdk.version>1.11</jdk.version>
<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
在项目上安装 Maven 包装器 也很有用(只需运行
mvn wrapper:wrapper
)。
对于 Gradle,我们应该确保 Gradle Shadow 插件存在于
plugins
文件的 build.gradle
部分,例如
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
apply plugin: 'java'
java {
sourceCompatibility = 1.11
targetCompatibility = 1.11
}
repositories {
mavenCentral()
}
test {
useJUnitPlatform()
}
dependencies {
implementation 'org.apache.commons:commons-lang3:3.13.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0'
}
创建 UberJars 的代码如下:
public class UberJar {
public static void createUberJarMaven() {
createUberJar("mvnw.cmd", "./mvnw", "package -DskipTests", "BUILD SUCCESS");
}
public static void createUberJarGradle() {
createUberJar("gradlew.bat", "./gradlew", "shadowJar", "BUILD SUCCESSFUL");
}
private static void createUberJar(String cmdWindows, String cmdLinux, String cmdBuild, String outputSuccess) {
String cmd = isWindows() ? cmdWindows : cmdLinux;
String output = execCmd(cmd + " " + cmdBuild);
if (output == null || !output.contains(outputSuccess)) {
throw new RuntimeException("Ubar JAR creation unsuccessful\n\n" + output);
}
}
private static String execCmd(String cmd) {
try {
Scanner s = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} catch (IOException ioEx) {
throw new RuntimeException("Error executing: " + cmd, ioEx);
}
}
private static boolean isWindows() {
return System.getProperty("os.name").toLowerCase().startsWith("windows");
}
}
测试这两者的单元测试如下:
public class UbarJarTest {
@Test
public void should_create_uber_jar_maven() {
UberJar.createUberJarMaven();
}
@Test
public void should_create_uber_jar_gradle() {
UberJar.createUberJarGradle();
}
}