Estoy reduciendo una gran aplicación web que incluía un servicio web para que se convierta solo en un servicio web de Jersey, en Spring Boot 1.5.2. Debido a que el servicio web ya tenía un conjunto completo de anotaciones JAX-RS implementadas por Apache Wink, decidí optar por Spring + Jersey en lugar de Spring Rest. Encontré esta aplicación spring-boot-jersey-sample para usarla como referencia. La mayor diferencia entre la aplicación en la que estoy trabajando y la muestra es que mis definiciones de punto final se dividen entre interfaces e implementaciones.
Agregué lo siguiente a mi 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>Mi nueva configuración de Jersey se ve así:
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); } }Entonces tengo el siguiente 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); } }Los puntos finales se definen como una interfaz y una implementación, así (menos las importaciones):
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(); } } Cuando inicio mi aplicación, veo mensajes que dicen que Tomcat se ha iniciado, incluido un mensaje que dice Mapping servlet: 'com.example.configuration.JerseyConfiguration' to [/*] . Sin embargo, cuando voy a / con un navegador web, aparece el error 404 Not Found . No parece que la definición GET esté siendo recogida.
Este problema se explica en la especificación JAX-RS en § 3.6 Herencia de anotación.
Las anotaciones JAX-RS pueden usarse en los métodos y parámetros de métodos de una superclase o una interfaz implementada. Dichas anotaciones son heredadas por un método de clase de implementación o subclase correspondiente, siempre que el método y sus parámetros no tengan anotaciones JAX-RS propias.
Si una subclase o un método de implementación tiene anotaciones JAX-RS, se ignoran todas las anotaciones en la superclase o el método de interfaz. P.ej:
public interface ReadOnlyAtomFeed { @GET @Produces("application/atom+xml") Feed getFeed(); } @Path("feed") public class ActivityLog implements ReadOnlyAtomFeed { public Feed getFeed() {...} }En lo anterior,
ActivityLog.getFeedhereda las anotaciones@GETy@Producesde la interfaz.En cambio:
@Path("feed") public class ActivityLog implements ReadOnlyAtomFeed { @Produces("application/atom+xml") public Feed getFeed() {...} }En lo anterior, la anotación
@GETenReadOnlyAtomFeed.getFeedno es heredada porActivityLog.getFeedy requeriría su propio designador de método de solicitud (@GET) ya que redefine la anotación@Produces.Para mantener la coherencia con otras especificaciones de Java EE, se recomienda repetir siempre las anotaciones en lugar de depender de la herencia de anotaciones.
He resaltado los puertos importantes. Debería quedar bastante claro por qué no funciona para usted. En su EndpointImpl , ha repetido las anotaciones de @Context , por lo tanto, "se ignoran todas las anotaciones en la superclase o el método de interfaz" . Esto incluye @GET . Entonces, en última instancia, esto hace que ese punto final no se registre, ya que los puntos finales requieren un @METHOD .
En cuanto al último párrafo de la cita en bloque, puede elegir seguirlo o no. Solo lo tiré allí solo para completar.
¿Cómo se ve tu application.yml (.properties):
Es posible que deba declarar dos asignaciones de ruta, una para el servlet despachador de Spring MVC y otra para el servlet despachador de Jersey . Algo como:
aplicación.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 ... Ahora debería poder acceder a los puntos finales de Jersey en /api/.....
Cubrí esto en una publicación de blog que publiqué en abril de 2016: Microservicios usando Spring Boot, Jersey, Swagger y Docker
A menos que algo haya cambiado en las versiones más recientes de Spring Boot/Spring MVC + Jersey, para que Spring Boot reconozca un punto final de Jersey, debe registrarlo así con un @Component adicional que amplíe ResourceConfig de Jersey:
@Component public class JerseyExampleConfig extends ResourceConfig { public JerseyExampleConfig() { register(EndpointImpl.class); } }