theo theo - 21 days ago 7
Java Question

How to make Jersey 2 / Spring 4 load beans from @Configuration on a no web.xml configuration

For the sake of learning (Spring Boot is great and magic..), I am trying to set up a web app using Jersey and Spring (for DI). I have managed to get it working but only with Spring xml configuration.

I have extended ResourceConfig

@ApplicationPath("/api")
public class JerseyWebApp extends ResourceConfig {

public JerseyWebApp() {
packages("com.company.controller");
}
}


My HelloController.java

@Path("/hello")
public class HelloController {

private HelloService helloService;

@Autowired
public HelloController(HelloService helloService) {
this.helloService = helloService;
}

@GET
public String hello() {
return helloService.sayHello();
}
}


my HelloService.java

@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class HelloService {

public String sayHello() {
return "hello from jersey webapp";
}
}


and my spring applicationContext.xml

<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">

<context:component-scan base-package="com.company" />
</beans>


Also attaching my pom file

<properties>
<jersey2.version>2.23.2</jersey2.version>
<jaxrs.version>2.0.1</jaxrs.version>
<spring.version>4.3.2.RELEASE</spring.version>
</properties>

<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>

<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>${jersey2.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-context</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-beans</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-core</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-web</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>jersey-server</artifactId>
<groupId>org.glassfish.jersey.core</groupId>
</exclusion>
<exclusion>
<artifactId>
jersey-container-servlet-core
</artifactId>
<groupId>org.glassfish.jersey.containers</groupId>
</exclusion>
<exclusion>
<artifactId>hk2</artifactId>
<groupId>org.glassfish.hk2</groupId>
</exclusion>
</exclusions>
</dependency>
<!--Spring 4-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>


Is it possible to get Jersey to load beans via a @Configuration class or shall I consider using Spring web/mvc to bootstrap the whole application.

Answer

The library jersey-spring3 for "some reason" in the file org.glassfish.jersey.server.spring.SpringWebApplicationInitializer.java is by default looking for a classpath resource:

sc.setInitParameter("contextConfigLocation", "classpath:applicationContext.xml");

and it does not support Java based Spring configurations. It is a known unsolved issue found here.

So to register Java based annotation configurations with Spring DI I had to workaround it and bootstrap my web app using a custom implementation of ServletContainerInitializer.

JerseyServletContainerInitializer.java

public class JerseyServletContainerInitializer implements ServletContainerInitializer {

  @Override
  public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {

    //Register spring configurations
    AnnotationConfigWebApplicationContext springContext = new AnnotationConfigWebApplicationContext();
    springContext.register(SpringConfig.class, JPAConfig.class);
    servletContext.addListener(new RequestContextListener());
    servletContext.addListener(new ContextLoaderListener(springContext));
    springContext.setServletContext(servletContext);

    //Register Jersey servlet
    ServletRegistration.Dynamic servletRegistration = servletContext.addServlet("JerseyServlet", new ServletContainer());
    servletRegistration.addMapping("/api/*");
    servletRegistration.setLoadOnStartup(1);
    servletRegistration.setInitParameter("javax.ws.rs.Application", JerseyResourceConfig.class.getName());
  }
}

JerseyResourceConfig.java

public class JerseyResourceConfig extends ResourceConfig {

  public JerseyResourceConfig() {
    packages("com.company.controller");
  }
}
Comments