如何在Maven+Kotlin/Java中程序化获取本地仓库Jar文件
如何在测试中跨机器读取本地Maven仓库的Jar文件?
问题场景
我想在测试运行时读取本地Maven仓库里的任意Jar文件,用完整硬编码路径能成功读取:
File("/Users/myusername/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar")
而且通过System.getProperty("java.class.path")能看到这个Jar已经在类路径列表里:
System.getProperty("java.class.path")
输出示例:
...:/Users/myusername/.m2/repository/io/netty/netty-transport-classes-epoll/4.1.87.Final/netty-transport-classes-epoll-4.1.87.Final.jar:/Users/myusername/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar
但我不想写死完整路径,要让代码能在任何装了Maven的机器上正常运行。试过直接用文件名读取:
File("log4j-1.2.17.jar")
结果找不到文件,因为默认会去应用类路径目录里查找,而不是Maven仓库。
额外背景
我用TestContainers做集成测试的容器构建和运行,想基于带Java的基础镜像,加上公司私有仓库的可执行Jar来构建GenericContainer。已经在pom.xml里引入了对应依赖:
<dependency> <groupId>private.group.id</groupId> <artifactId>private.artifact.id</artifactId> <scope>test</scope> <classifier>exec</classifier> <version>${jar.version}</version> </dependency>
当前构建容器的代码示例:
GenericContainer<Nothing>( ImageFromDockerfile() .withFileFromFile(jarName, File("/Users/username/.m2/repository/groupId/artifactId/version/$jarName")) .withDockerfileFromBuilder { builder: DockerfileBuilder -> builder .from("urlToBaseImageWithJava") .copy(jarName,"/app/$jarName") .entryPoint("java", "-jar", "/app/$jarName") .build() } ).apply { withNetwork(myNetwork) start() }
解决方法
方法1:通过类加载器定位Jar文件
利用Jar里的类反向定位Jar路径,只要Jar在类路径中就能生效,完全不需要硬编码路径:
// 替换成你依赖Jar里任意一个类的全限定名 val targetClass = Class.forName("com.your.package.YourClassFromExecJar") val jarUrl = targetClass.protectionDomain.codeSource.location val jarFile = File(jarUrl.toURI())
拿到这个jarFile后,就能直接传给TestContainers的withFileFromFile方法。
方法2:动态获取Maven仓库路径
Maven仓库的路径可以通过系统属性或默认规则动态获取,再拼接Jar的完整路径:
// 优先取系统属性中配置的Maven仓库路径,没有则用默认的~/.m2/repository val mavenRepo = System.getProperty("maven.repo.local") ?: "${System.getProperty("user.home")}${File.separator}.m2${File.separator}repository" // 把groupId的点换成路径分隔符,拼接完整Jar路径 val jarFullPath = "$mavenRepo${File.separator}private${File.separator}group${File.separator}id${File.separator}private${File.separator}artifact${File.separator}id${File.separator}${jar.version}${File.separator}private.artifact.id-${jar.version}-exec.jar" val jarFile = File(jarFullPath)
注意要根据你的依赖信息,把groupId、artifactId、version和classifier对应到路径里。
方法3:用Maven插件把Jar复制到本地目录
在pom.xml中配置maven-dependency-plugin,把依赖的可执行Jar复制到测试资源目录,之后就能用相对路径读取:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>copy-exec-jar</id> <phase>process-test-resources</phase> <goals> <goal>copy</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>private.group.id</groupId> <artifactId>private.artifact.id</artifactId> <version>${jar.version}</version> <classifier>exec</classifier> <outputDirectory>${project.build.directory}/test-resources</outputDirectory> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build>
测试代码里直接读取复制后的Jar:
val jarPath = "${System.getProperty("user.dir")}${File.separator}target${File.separator}test-resources${File.separator}private.artifact.id-${jar.version}-exec.jar" val jarFile = File(jarPath)
内容的提问来源于stack exchange,提问作者DJ-Glock
相关产品推荐
相关产品推荐

