Embedded Jetty+2个Jersey Servlet:导出Jar后第二个服务报500错误求助
看起来你遇到的核心问题是Maven Shade插件打包时的资源文件冲突——Eclipse运行时依赖是分散加载的,而Shade打包成单一JAR时,默认会覆盖重复的资源文件,尤其是Jersey依赖的SPI(服务提供者接口)配置文件,这直接导致其中一个Servlet无法正常加载所需的Jersey组件。
以下是分步解决方案:
1. 修复Maven Shade插件的资源合并配置
Jersey通过META-INF/services目录下的SPI文件加载服务实现,Shade插件默认会用最后一个JAR中的同名文件覆盖之前的,导致部分服务条目丢失。你需要添加ServicesResourceTransformer来合并这些文件:
在你的pom.xml中更新Shade插件配置:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <!-- 指定你的主类(替换成实际的主类路径) --> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.yourpackage.YourMainClass</mainClass> </transformer> <!-- 合并所有META-INF/services下的SPI文件 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <!-- 合并Jersey的版本信息文件,避免冲突 --> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/jersey-module-version</resource> </transformer> </transformers> </configuration> </execution> </executions> </plugin>
2. 优化Jetty中Jersey Servlet的注册方式
你当前通过initParameter指定provider类名的方式,在打包后可能出现类路径查找问题。改用ResourceConfig直接注册端点类会更可靠:
// 配置第一个Jersey应用 ResourceConfig entryResourceConfig = new ResourceConfig(); entryResourceConfig.register(EntryPoint.class); ServletHolder entryServletHolder = new ServletHolder(new ServletContainer(entryResourceConfig)); entryServletHolder.setInitOrder(0); context.addServlet(entryServletHolder, "/entry/*"); // 配置第二个Jersey应用 ResourceConfig noEntryResourceConfig = new ResourceConfig(); noEntryResourceConfig.register(NoEntryPoint.class); ServletHolder noEntryServletHolder = new ServletHolder(new ServletContainer(noEntryResourceConfig)); noEntryServletHolder.setInitOrder(1); context.addServlet(noEntryServletHolder, "/noentry/*");
这种方式跳过了类名的字符串查找,直接通过代码注册类实例,减少了类路径相关的问题。
3. 启用详细日志定位深层问题
因为你运行JAR时控制台没有报错,大概率是日志配置未生效,无法看到Jersey内部的错误堆栈。添加SLF4J+Logback依赖来开启DEBUG日志:
在pom.xml中添加依赖:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency>
然后在src/main/resources下创建logback.xml:
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 开启Jersey的DEBUG日志 --> <logger name="org.glassfish.jersey" level="DEBUG"/> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>
重新打包后运行JAR,就能看到Jersey处理请求时的详细错误信息,方便进一步排查。
为什么Eclipse中运行正常?
Eclipse是直接从项目的类目录和依赖JAR中加载资源,每个依赖的SPI文件都是独立存在的,不会被覆盖;而Shade插件将所有依赖合并到一个JAR中,默认的合并策略会覆盖重复资源,导致Jersey无法找到完整的服务实现。
内容的提问来源于stack exchange,提问作者Zineta Husic

