Jersy/Jetty 服务器的 MessageBodyWriter 未找到错误

2022-01-21 00:00:00 rest gradle java jersey jetty

我一直在关注 本教程在 Jetty 上使用 Jersey 创建 REST API,我喜欢这个结果.一切正常.但是,如果我运行 Gradle shadowJar 任务来生成一个胖 jar 文件,这可以正常工作,并且该文件也可以运行,但在发出请求时会以错误消息结束:

I've just been following this tutorial to create a REST API using Jersey on Jetty and I like the result. Everything works fine. But if I run the Gradle shadowJar task to generate a fat jar file this works fine and the file also runs but ends with an error message when a request is made:

$ java -jar build/libs/sample-all.jar 
[main] INFO org.eclipse.jetty.util.log - Logging initialized @161ms to org.eclipse.jetty.util.log.Slf4jLog
[main] INFO org.eclipse.jetty.server.Server - jetty-9.4.z-SNAPSHOT
[main] INFO org.eclipse.jetty.server.handler.ContextHandler - Started o.e.j.s.ServletContextHandler@5824a83d{/,null,AVAILABLE}
[main] INFO org.eclipse.jetty.server.AbstractConnector - Started ServerConnector@2ddc9a9f{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
[main] INFO org.eclipse.jetty.server.Server - Started @1547ms
Dez 12, 2018 10:05:07 PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
SCHWERWIEGEND: MessageBodyWriter not found for media type=application/json, type=class com.dovydasvenckus.jersey.greeting.Greeting, genericType=class com.dovydasvenckus.jersey.greeting.Greeting.

所以在我看来,在编译的 JAR 中,有些例如缺少库.我在网上环顾四周,每个人都建议添加 org.glassfish.jersey.media:jersey-media-json-jackson:2.27 (或一些变体)以使其工作.但这对我不起作用.我的 build.gradle 看起来像:

So in the compiled JAR it seems to me, that some e.g. lib is missing. I looked around in the web and everybody suggests to add org.glassfish.jersey.media:jersey-media-json-jackson:2.27 (or some variations) to make it work. But this does not work for me. My build.gradle looks like:

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

sourceCompatibility = 1.8
mainClassName = 'com.dovydasvenckus.jersey.JerseyApplication'

ext {
    slf4jVersion = '1.7.25'
    jettyVersion = '9.4.6.v20170531'
    jerseyVersion = '2.27'
}

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
    }
}

repositories {
    jcenter()
}

dependencies {
    compile "org.slf4j:slf4j-api:${slf4jVersion}"
    compile "org.slf4j:slf4j-simple:${slf4jVersion}"

    compile "org.eclipse.jetty:jetty-server:${jettyVersion}"
    compile "org.eclipse.jetty:jetty-servlet:${jettyVersion}"

    compile "org.glassfish.jersey.core:jersey-server:${jerseyVersion}"
    compile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jerseyVersion}"
    compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jerseyVersion}"
    compile "org.glassfish.jersey.media:jersey-media-json-jackson:${jerseyVersion}"
    compile "org.glassfish.jersey.inject:jersey-hk2:${jerseyVersion}"

    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.27'

    compile "org.glassfish.jersey.media:jersey-media-moxy:2.27"

    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.27'

}

jar { manifest { attributes 'Main-Class': "${mainClassName}" } }

有什么让它发挥作用的想法吗?

推荐答案

如 我的答案 中所述="https://stackoverflow.com/q/37735728/2587435">MessageBodyProviderNotFoundException while running jar from command line,有一个服务文件",即org.glassfish.jersey.internal.spi.AutoDiscoverable,它包含在许多 jar 中.此文件的目的是允许 Jersey(和其他第三方)jar 为这些 jar 中包含的功能提供一些自动注册.这包括注册 JacksonFeature,它注册处理(反)序列化的 JSON 提供程序.

As mentioned in my answer in MessageBodyProviderNotFoundException while running jar from command line, there is a "services file", namely org.glassfish.jersey.internal.spi.AutoDiscoverable, which is included in many jars. The purpose of this file is to allow Jersey (and other third party) jars to provide some auto-registration for features included in those jars. This includes registration of the JacksonFeature, which registers the JSON providers that handle (de)serialization.

创建胖 (uber) jar 的问题在于只能有一个文件(您不能拥有多个同名文件).因此,对于 fat jar 中包含的所有 jar,只会包含一个服务文件.

The problem with creating fat (uber) jars is that there can only be one of those files (you can't have more than one file of the same name). So with all the jars included in the fat jar, only one service file will be included.

对于 Maven,我们将使用 maven-shade-plugin,它具有允许转换构建部分的转换器.shade 插件有一个 ServicesResourceTransformer 负责将服务文件的内容连接到一个服务文件中.您用于 Gradle 的插件 Shadow 具有相同的功能.您可以通过在配置中调用 mergeServiceFiles() 来配置它.我并没有真正使用 Gradle,但在 this issue 中也有说明,推荐处理服务文件转换和主类清单转换的配置如下

With Maven, we would use the maven-shade-plugin, which has transformers that allow for transforming parts of the build. The shade plugin has a ServicesResourceTransformer which takes care of concatenating the contents of the service files into one service file. The plugin you are using for Gradle, Shadow, has the same facility. You configure it by calling mergeServiceFiles() in the configuration. I don't really work with Gradle, but also stated in this issue, the recommended configuration for handling both the service files transformation and the main class manifest transformation is the following

shadowJar {
  mergeServiceFiles()
  manifest {
    attributes 'Main-Class': 'com.my.Application'
  }
}

所以我假设,通过上面的配置,你也可以删除

So I am assuming, with the above configuration, you can also remove

jar { manifest { attributes 'Main-Class': "${mainClassName}" } }

因为 Shadow 插件将负责构建清单.同样,我并没有真正使用 Gradle,所以这只是一个假设;但听起来不错.

as the Shadow plugin will take care of building the manifest. Again, I don't really use Gradle, so this is just an assumption; but it sounds right.

相关文章