JAX-RS 不适用于 Spring Boot 1.4.1
Posted
技术标签:
【中文标题】JAX-RS 不适用于 Spring Boot 1.4.1【英文标题】:JAX-RS does not work with Spring Boot 1.4.1 【发布时间】:2017-02-16 01:19:18 【问题描述】:我正在尝试使用 Spring Boot 版本 1.4.1.RELEASE 开发一个简单的基于 JAX-RS 的 Web 服务。但是得到这个异常 -
java.lang.IllegalStateException: No generator was provided and there is no default generator registered
at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.internalCreate(ServiceLocatorFactoryImpl.java:308) ~[hk2-api-2.5.0-b05.jar:na]
at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.create(ServiceLocatorFactoryImpl.java:268) ~[hk2-api-2.5.0-b05.jar:na]
at org.glassfish.jersey.internal.inject.Injections._createLocator(Injections.java:138) ~[jersey-common-2.23.2.jar:na]
at org.glassfish.jersey.internal.inject.Injections.createLocator(Injections.java:123) ~[jersey-common-2.23.2.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:330) ~[jersey-server-2.23.2.jar:na]
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:392) ~[jersey-container-servlet-core-2.23.2.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:177) ~[jersey-container-servlet-core-2.23.2.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:369) ~[jersey-container-servlet-core-2.23.2.jar:na]
这是我的计划详情 -
POM.xml 中包含的依赖项 -
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
这里是 JerseyConfig 文件 -
package com.test.main;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
import com.test.resources.TutorialResource;
@Component
public class JerseyConfig extends ResourceConfig
public JerseyConfig()
register(TutorialResource.class);
packages("com.test.resources");
【问题讨论】:
【参考方案1】:重要提示:在最新版本的 Spring Boot 中似乎不存在此问题。但是,当您想使用 Spring Boot 和 Jersey 创建应用程序时,此答案的内容仍然可以用作指南。
JAR 的布局在 Spring Boot 1.4.1 中发生了变化
Spring Boot 1.4.1 中的layout of executable jars has changed:应用程序的依赖项现在被打包在BOOT-INF/lib
而不是lib
,应用程序自己的类现在被打包在BOOT-INF/classes
而不是jar 的根目录中。它会影响泽西岛:
Jersey classpath scanning limitations
对可执行 jar 布局的更改意味着 limitation in Jersey’s classpath scanning 现在会影响可执行 jar 文件以及可执行 war 文件。要解决此问题,您希望由 Jersey 扫描的类应打包在一个 jar 中,并作为依赖项包含在
BOOT-INF/lib
中。然后 Spring Boot 启动器应该是 configured to unpack those jars on start up 以便 Jersey 可以扫描它们的内容。
我发现注册类而不是包是可行的。请参阅下面使用 Spring Boot 和 Jersey 创建应用程序的步骤。
使用 Spring Boot 和 Jersey 创建 Web 应用程序
确保您的 pom.xml
文件将 spring-boot-starter-parent
声明为父项目:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
您还需要以下依赖项:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
还有 Spring Boot Maven 插件:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
例如,创建一个带有 @Path
注释的 Jersey 资源类,并定义一个资源方法来处理 GET
请求,生成 text/plain
:
@Path("/greetings")
public class GreetingResource
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response getGreeting()
return Response.ok("Hello, World!").build();
然后创建一个扩展ResourceConfig
或Application
的类来注册Jersey资源并用@ApplicationPath
注解。注册类而不是注册包适用于 Spring Boot 1.4.1:
@Component
@ApplicationPath("api")
public class JerseyConfig extends ResourceConfig
@PostConstruct
private void init()
registerClasses(GreetingResource.class);
最后创建一个 Spring Boot 类来执行应用程序:
@SpringBootApplication
public class Application
public static void main(String[] args)
SpringApplication.run(Application.class, args);
如果你想测试这个网络服务,你可以使用JAX-RS Client API:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GreetingResourceTest
@LocalServerPort
private int port;
private URI uri;
@Before
public void setUp() throws Exception
this.uri = new URI("http://localhost:" + port);
@Test
public void testGreeting()
Client client = ClientBuilder.newClient();
Response response = client.target(uri).path("api").path("greetings")
.request(MediaType.TEXT_PLAIN).get();
String entity = response.readEntity(String.class);
assertEquals("Hello, World!", entity);
要编译和运行应用程序,请按以下步骤操作:
打开命令行窗口或终端。 导航到项目的根目录,pom.xml
所在的位置。
编译项目:mvn clean compile
。
打包应用程序:mvn package
。
查看目标目录。您应该会看到具有以下名称或类似名称的文件:spring-jersey-1.0-SNAPSHOT.jar
。
切换到目标目录。
执行 JAR:java -jar spring-jersey-1.0-SNAPSHOT.jar
。
应用程序应在http://localhost:8080/api/greetings
提供。
注意 1:查看 Spring Boot 文档。有一个section dedicated to Jersey。
注意 2: 生成 JSON 时,请确保您有 JSON provider registered。 ResourceConfig
应该注意这一点(只需确保依赖项位于类路径上)。
【讨论】:
感谢 Cassio 的全面回复。但是,除了 Maven 依赖项之外,我们俩似乎都遵循了相同的步骤。我使用的是 spring-boot-starter-jersey,而你使用的是 jersey-spring3。您可以尝试使用前者吗,因为我倾向于在我的设置中使用它。 @akashmkr6 我刚刚用spring-boot-starter-jersey
进行了测试,它可以工作。我的答案已更新。
这应该是开始使用 spring boot 和 jax-rs 的一个很好的精确示例,但是当我尝试使用 2.0.1 版本时,我收到错误“ClassNotFoundException:org.eclipse.yasson.JsonBindingProvider” .可能出了什么问题?【参考方案2】:
虽然 Jersey 无法扫描新版本的 fat boot jar 中的类,但您可以使用 Spring 类路径扫描工具实现相同的效果。这样你就可以像ResourceConfig.packages()
一样扫描一个包:
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
scanner.addIncludeFilter(new AnnotationTypeFilter(Path.class));
config.registerClasses(scanner.findCandidateComponents("your.package.to.scan").stream()
.map(beanDefinition -> ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), config.getClassLoader()))
.collect(Collectors.toSet()));
注意:请查看org.glassfish.jersey.server.internal.scanning.AnnotationAcceptingListener
的来源。这是常用的解决方案,您可以看到它的作用是相同的:它扫描带有 @Path
或 @Provider
注释的类(但由于扫描机制损坏,无法找到任何东西)。
更新:
我有一个自定义配置,它没有扩展 ResourceConfig,而是将它的一个实例作为 bean 返回。
如果你看官方的Spring example,你可以将上面的代码插入到JerseyConfig()
构造函数中(而不是两个register(...)
调用)。唯一的区别是,您只需在构造函数中调用registerClasses(...)
,而不是调用config.registerClasses(...)
。
【讨论】:
这是一个完美而干净的解决方法:采用!谢谢! 这适用于 Jersey 1.x 吗?还有那个代码去哪儿了? 我只用 Jersey 2.x 测试过这个。我已经更新了答案以建议可以插入的位置。【参考方案3】:我认为您应该使用 @Configuration
而不是 @Component
来注释您的 JerseyConfig
。
【讨论】:
JerseyConfig
应该用@Component
和@ApplicationPath
注释。 JAR 的布局在 Spring Boot 1.4.1 中发生了变化,这种变化会导致其他问题。请参阅my answer 了解解决方法。以上是关于JAX-RS 不适用于 Spring Boot 1.4.1的主要内容,如果未能解决你的问题,请参考以下文章
Thymeleaf 安全不适用于 Spring Boot 1.3.5