How to use Spring Boot 1.5.2 to inject implementations of Jersey endpoint interfaces?

倾然丶 夕夏残阳落幕 提交于 2019-12-29 09:27:28

问题


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 on ReadOnlyAtomFeed.getFeed is not inherited by ActivityLog.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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!