with springservlet servlet container spring dependency-injection nullpointerexception jersey autowired

springservlet - Spring DI: la propiedad de Autowired es nula en un servicio REST



spring jersey (4)

¡Usted tenía razón! Parece que el problema es que Jersey ignora por completo a Spring y crea una instancia de su propio objeto. Para hacer que Jersey fuera consciente de las creaciones de objetos Spring (mediante inyección de dependencia) tuve que integrar Spring + Jersey.

Integrar:

  1. Añadir dependencias maven

    <dependency> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-spring</artifactId> <version>1.17.1</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </exclusion> </exclusions> </dependency>

  2. Use SpringServlet para jersey-servlet en web.xml

    <servlet> <servlet-name>jersey-servlet</servlet-name> <servlet-class> com.sun.jersey.spi.spring.container.servlet.SpringServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet>

Ahora el @Autowired funciona correctamente y el objeto ya no es nulo.

Estoy un poco confundido acerca de las exclusiones que tengo que usar en Maven cuando uso la dependencia de jersey-spring, pero ese es otro problema :)

¡Gracias!

Estoy empezando con Spring DI, pero estoy luchando con la inyección de dependencia y lo peor es que ni siquiera estoy seguro de por qué, ya que me parece bien. ¡Espero que ustedes me puedan ayudar!

El problema es que una propiedad anotada como @Autowired siempre es nula

Tengo algunos proyectos con estructura Maven:

  • com.diegotutor.lessondeliver
  • com.diegotutor.utility

Estoy ejecutando los ejemplos sobre Tomcat 7

Estoy usando las siguientes dependencias en mi pom.xml :

  • contexto de primavera 3.2.4
  • web de primavera 3.2.4
  • jersey-server 1.17.1
  • jersey-core 1.17.1
  • jersey-servlet 1.17.1

La idea simple es tener un servicio REST que, a través de la inyección de dependencia, pueda imprimir el valor de una propiedad ubicada en un archivo de configuración ubicado en: D:/configuracion.conf.

En com.diegotutor.utility tengo la siguiente interfaz:

package com.diegotutor.utility; public interface ConfigService { public String getProperty(final String propertyName); }

Implementado por:

package com.diegotutor.utility.impl; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.Properties; import com.diegotutor.utility.ConfigService; public class PropertyFileConfigService implements ConfigService{ Properties prop; public PropertyFileConfigService (final InputStream input) throws IOException { if(input == null) { throw new IllegalArgumentException("Input stream can''t be null"); } prop = new Properties(); prop.load(input); } public PropertyFileConfigService (final String fileName) throws IOException { final FileInputStream input = new FileInputStream(fileName); prop = new Properties(); prop.load(input); } public PropertyFileConfigService(final Reader input) throws IOException { prop = new Properties(); prop.load(input); } public String getProperty(final String propertyName) { return prop.getProperty(propertyName); } }

Y en com.diegotutor.lessondeliver tengo el servicio RESTful en el que me gustaría usar una instancia inyectada de ConfigService:

package com.diegotutor.lessondeliver; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.diegotutor.utility.ConfigService; @Path("/") @Component public class HelloWorld { private static final Log log = LogFactory.getLog(HelloWorld.class); @Autowired private ConfigService configService; @Path("/helloworld") @GET @Produces(MediaType.TEXT_PLAIN) public String getHello() { String host = configService.getProperty("host"); return "Hello World! HOST" + host; // configService IS NULL!! //SO IT THROWS A NULLPOINTER EXCEPTION WHEN INVOKING getProperty ON IT } }

Finalmente, en /com.diegotutor.lessondeliver/src/main/webapp/WEB-INF/service-beans.xml tengo el siguiente archivo de contexto de aplicación XML, donde uso la implementación de ConfigService (PropertyFileConfigService) inyectando en ella la ruta para la archivo de configuración para leer:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="configService" class="com.diegotutor.utility.impl.PropertyFileConfigService"> <constructor-arg type="java.lang.String" value="D:/configuracion.conf" /> </bean> <context:component-scan base-package="com.diegotutor" /> </beans>

Obviamente, he especificado en el web.xml de esta aplicación web com.diegotutor.lessondeliver que deseo service-beans.xml como ConfigLocation y un oyente ContextLoaderListener , y el servicio RESTful se basa en ServletContainer

Si estoy especificando context: component-scan para buscar Componentes en com.diegotutor como se sugiere here y estoy forzando la creación de objetos a través de Spring al no usar ninguna Declaración nueva como se sugiere here , ¿Por qué obtengo el servicio de configuración anotado como nulo? ¿Por qué Spring no puede inyectar una instancia de com.diegotutor.utility.impl.PropertyFileConfigService ?

¡Cualquier ayuda será muy apreciada!

Gracias

EDITADO:

Según lo solicitado, mi web.xml es el siguiente:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>com.diegotutor.lessondeliver</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/service-beans.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>jersey-servlet</servlet-name> <servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey-servlet</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app>


Integración Spring con Jersey 2 ( org.glassfish.* ):

Maven

Algunas dependencias pueden ser innecesarias, verifíquelas y elimínelas después de que las cosas funcionen.

<properties> <jersey.version>2.5</jersey.version> </properties> <!-- Jersey --> <dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-server</artifactId> <version>${jersey.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <!-- if your container implements Servlet API older than 3.0, use "jersey-container-servlet-core" --> <artifactId>jersey-container-servlet</artifactId> <version>${jersey.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-client</artifactId> <version>${jersey.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> <artifactId>jersey-test-framework-provider-inmemory</artifactId> <version>${jersey.version}</version> </dependency> <!-- Jersey + Spring --> <dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-spring3</artifactId> <version>${jersey.version}</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> </exclusions> </dependency>

web.xml

<servlet> <servlet-name>my-rest-service</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>my.package.with.rest.services</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>my-rest-service</servlet-name> <url-pattern>/api/*</url-pattern> </servlet-mapping>

applicationContext.xml

Durante la actualización de Spring tuve que moverlo de /main/webapp/WEB-INF/ a /main/resources/ ( details ).

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="my.package.with.rest.services" /> </beans>

Ejemplo de servicio REST

public interface MyService { String work(String s); } ... @Service public class MyServiceImpl implements MyService { @Override public String work(String s) { return "Hello, " + s; } } ... @Path("demo/") @Component public class DemoRestService { @Autowired private MyService service; @GET @Path("test") public Response test(@FormParam("param") String par) { try { String entity = service.work(par); return Response.ok(entity).build(); } catch (Exception e) { e.printStackTrace(); return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Epic REST Failure").build(); } } }


Otra opción posible es invocar manualmente el cableado automático en su recurso de jersey:

@Context private ServletContext servletContext; @PostConstruct public void init() { SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, servletContext); }

Hmm, obtienes un "cableado manual" ...


o simplemente puede extender la clase SpringBeanAutoWiringSupport. Así: public class DemoRestService extends SpringBeanAutoWiringSupport . Al extender esta clase de soporte, las propiedades de su clase de servicio se pueden conectar automáticamente.