基于AsyncHttpClient的Java Maven项目运行JNI错误排查求助
错误原因分析
你遇到的java.lang.NoClassDefFoundError: org/asynchttpclient/AsyncHandler错误,本质是因为你打包生成的JAR只包含了你自己编写的代码,没有把项目依赖的AsyncHttpClient等第三方库打包进去。当Java运行时尝试加载这些依赖类时,找不到对应的class文件,就会抛出这个错误。
解决方案:打包包含所有依赖的可执行JAR
Maven默认的package命令只会生成只含自身代码的JAR,我们需要用Maven插件来构建包含所有依赖的"胖JAR"(Fat JAR)。这里推荐两种常用的插件:
1. 使用Maven Shade Plugin(推荐)
Shade插件不仅会打包所有依赖,还能处理可能的类冲突,是比较常用的选择。在你的pom.xml中添加以下插件配置:
<build> <plugins> <!-- 配置Shade插件构建可执行胖JAR --> <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> <configuration> <!-- 指定主类,也就是你项目的入口类 --> <mainClass>me.company.MyProject.App</mainClass> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>me.company.MyProject.App</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
添加完成后,重新执行Maven的package命令:
mvn clean package
执行完成后,在target目录下会生成两个JAR:
MyProject-0.0.1-SNAPSHOT.jar:默认的轻量JAR(不含依赖)MyProject-0.0.1-SNAPSHOT-shaded.jar:包含所有依赖的可执行胖JAR
运行这个胖JAR即可:
java -jar .\MyProject-0.0.1-SNAPSHOT-shaded.jar
2. 使用Maven Assembly Plugin
如果你更倾向于另一种方式,也可以用Assembly插件。在pom.xml中添加:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.6.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>me.company.MyProject.App</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
同样执行mvn clean package,然后运行生成的MyProject-0.0.1-SNAPSHOT-jar-with-dependencies.jar:
java -jar .\MyProject-0.0.1-SNAPSHOT-jar-with-dependencies.jar
额外代码优化提示
你编写的AsyncHandler实现有个小问题:onStatusReceived、onHeadersReceived、onBodyPartReceived方法返回的是null,这会导致运行时异常。根据AsyncHttpClient的要求,这些方法需要返回一个State枚举值,比如State.CONTINUE表示继续处理响应,State.ABORT表示中止处理。修改后的代码示例:
postRequest.execute(new AsyncHandler<Object>() { public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception { System.out.print("\nstatus received: " + responseStatus.getStatusCode()); return State.CONTINUE; // 必须返回有效的State值 } public State onHeadersReceived(HttpHeaders headers) throws Exception { System.out.print("\nheaders received"); return State.CONTINUE; } public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { System.out.print("\nbody part received"); return State.CONTINUE; } public void onThrowable(Throwable t) { t.printStackTrace(); // 建议添加异常打印,方便排查问题 } public Object onCompleted() throws Exception { System.out.print("\nexecute completed"); return null; } });
另外,注意不要在发送异步请求后立即关闭client,因为异步请求还没完成就关闭客户端会导致请求失败。应该在请求完成后(比如onCompleted或onThrowable方法中)关闭客户端,或者使用try-with-resources来管理:
try (AsyncHttpClient client = Dsl.asyncHttpClient()) { // 构建并执行请求 BoundRequestBuilder postRequest = client .preparePost("http://localhost:8080/my_custom_url/") .addFormParam("id", myid) .addFormParam("stream", inData); postRequest.execute(new AsyncHandler<Object>() { // 实现方法... public Object onCompleted() throws Exception { System.out.print("\nexecute completed"); return null; } }).toCompletableFuture().join(); // 如果需要等待请求完成,可使用join() } catch (IOException e) { // 处理异常 }
这样可以避免因提前关闭客户端导致的请求失败问题。
内容的提问来源于stack exchange,提问作者JumpingElephant

