问题
I am scaling back a large web application that included a web service to become only a Jersey web service, on Spring Boot 1.5.2. Because the web service already had a complete set of JAX-RS annotations implemented by Apache Wink, I decided to go with Spring + Jersey instead of Spring Rest. I found this spring-boot-jersey-sample application to use as a reference. The biggest difference between the application I'm working on and the sample is that my endpoint definitions are divided between interfaces and implementations.
I added the following to my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
My new Jersey Config looks like this:
package com.example.configuration;
import org.glassfish.jersey.server.ResourceConfig;
import com.example.EndpointImpl;
import org.springframework.stereotype.Component;
@Component
public class JerseyConfiguration extends ResourceConfig {
public JerseyConfiguration() {
registerEndpoints();
}
private void registerEndpoints() {
register(EndpointImpl.class);
}
}
Then I have the following Application.java:
package com.example;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
@SpringBootApplication
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
new Application().configure(new SpringApplicationBuilder(Application.class)).run(args);
}
}
The endpoints are defined as an interface and an implementation, like this (minus imports):
public interface Endpoint {
@GET
@Produces({MediaType.APPLICATION_JSON})
public Response getHello(@Context ServletContext sc, @Context HttpServletRequest req, @Context HttpHeaders httpHeaders) ;
}
@Path("")
@Component
public class EndpointImpl implements Endpoint {
@Override
public Response getHello(@Context ServletContext sc, @Context HttpServletRequest req,
@Context HttpHeaders httpHeaders) {
return Response.ok("hello").build();
}
}
When I start up my application, I see messages saying Tomcat has started up, including a messges saying Mapping servlet: 'com.example.configuration.JerseyConfiguration' to [/*]
. However, when I go to / with a web browser, I get a 404 Not Found
error. It doesn't look like the GET definition is getting picked up.
回答1:
This problem is explained in the JAX-RS spec in § 3.6 Annotation Inheritance.
JAX-RS annotations may be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that the method and its parameters do not have any JAX-RS annotations of their own.
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored. E.g.:
public interface ReadOnlyAtomFeed { @GET @Produces("application/atom+xml") Feed getFeed(); } @Path("feed") public class ActivityLog implements ReadOnlyAtomFeed { public Feed getFeed() {...} }
In the above,
ActivityLog.getFeed
inherits the@GET
and@Produces
annotations from the interface.Conversely:
@Path("feed") public class ActivityLog implements ReadOnlyAtomFeed { @Produces("application/atom+xml") public Feed getFeed() {...} }
In the above, the
@GET
annotation onReadOnlyAtomFeed.getFeed
is not inherited byActivityLog.getFeed
and it would require its own request method designator (@GET
) since it redefines the@Produces
annotation.For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
I've highlighted the important ports. It should be pretty clear why it isn't working for you. In your EndpointImpl
, you have repeated the @Context
annotations, therefore causing "all of the annotations on the superclass or interface method are ignored". This include the @GET
. So ultimately, this causes that endpoint not to be registered, as endpoints require a @METHOD
.
As far as the last paragraph in the blockquote, you can choose to follow it or not. I just threw it in there just for completeness.
回答2:
What does your application.yml (.properties) look like:
You might need to declare two path mappings, one for Spring
MVC dispatcher servlet and one for Jersey
dispatcher servlet. Something like:
application.yml
...
# Spring MVC dispatcher servlet path. Needs to be different than Jersey's to enable/disable Actuator endpoints access (/info, /health, ...)
server.servlet-path: /
# Jersey dispatcher servlet
spring.jersey.application-path: /api
...
You should now be able to access Jersey
endpoints at /api/.....
I covered this in a blog post I published back in Apr 2016: Microservices using Spring Boot, Jersey, Swagger and Docker
回答3:
Unless something has changed in more recent Spring Boot/Spring MVC + Jersey versions, to get Spring Boot to recognize a Jersey endpoint you need to register it like this with an additional @Component that extends Jersey's ResourceConfig:
@Component
public class JerseyExampleConfig extends ResourceConfig {
public JerseyExampleConfig() {
register(EndpointImpl.class);
}
}
来源:https://stackoverflow.com/questions/43266904/how-to-use-spring-boot-1-5-2-to-inject-implementations-of-jersey-endpoint-interf