Hi,
after jersey 0.7 was released on friday, I just finished writing tests for the spring-integration stuff.
The jersey-spring-integration is based on the SpringComponentProvider blogged by Paul Sandoz and adds some nice things like annotation based injection of IoC-container managed beans (useful for all IoC-frameworks) and annotation based autowiring support even for spring-2.0 users.
Some extensions were made to jersey itself, which are useful for integration of IoC-containers in general (like the mentioned annotation based injection of beans).
The jersey-spring-integration is developed in a separate branch and is going to be part of jersey release 0.8. If you want to use it before jersey 0.8 is there, you can checkout the branch, build jersey, then spring and use the built jars (and configure your web.xml accordingly as described below).
So what does the jersey-spring-integration provide?
- Integration of spring-2.0 or spring-2.5 with jersey (what else 8-))
- jersey resources can be managed by spring (pulled from spring’s bean-factory)
- jersey resources can use spring managed beans via the
@Inject annotation that is now provided by jersey (also useful for integration of other IoC-containers)
- jersey resources can be marked with the
@Autowire annotation (that is provided by the jersey-spring-integration), such resources then are created by spring and dependencies injected accordingly
- jersey resources can use annotation based configuration that comes with spring-2.5
- jersey now provides an
AnnotationInjectable, that can be provided for some annotation, so that the injectable is asked for an instance of a class if a field of some resource is annotated with the specified annotation
- jersey now provides a
ResourceContext, that can be used to get an instance of some subresource from the IoC-container and therfore can use features like e.g. AOP provided by the IoC-container also for subresources
Integration of spring with jersey
After you built both jersey and jersey-spring from the spring-integration branch and configured them as dependencies for your project, you only have to register the SpringServlet in your web.xml (I’m asuming that you have spring already configured therein):
<servlet>
<servlet-name>Jersey Spring</servlet-name>
<servlet-class>com.sun.ws.rest.spring.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.ws.rest.config.property.resourceConfigClass</param-name>
<param-value>com.sun.ws.rest.api.core.PackagesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.ws.rest.config.property.packages</param-name>
<param-value>your.resource.package</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Spring</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
Use resources managed by spring
If you want to get your resource created by spring you simply have to define it in your applicationContext.xml. Only bear in mind, that the scope in your applicationContext.xml matches the scope of your resource class (jersey-Singleton == spring-singleton, jersey-PerRequest == spring-prototype).
For example you might have this resource:
@Path( "springifiedsingleton" )
@Singleton
public class SpringifiedSingletonRootResource {
private String _value;
public SpringifiedSingletonRootResource String value ) {
_value = value;
}
@GET
@ProduceMime("text/plain")
public String getValue() {
return _value;
}
}
Define the resource in your applicationContext.xml:
<bean id="singletonRootResource" class="spring.examples.SpringifiedSingletonRootResource"
scope="singleton">
<constructor-arg value="foo" />
</bean>
That’s it, now your resources will be provided by spring.
If you don’t want to define each new resource in your applicationContext, but simply want to use your existing spring managed beans/services, you can use the following approach using the @Inject annotation.
@Inject spring managed beans/services into your resources
The @Inject annotation is now provided by jersey and is useful for integration of several IoC-containers, not only for spring. A field that is annotated with @Inject is initialized with an instance (of the fields type), that is pulled from the ComponentProvider, and in this case from spring.
Just to have an example:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;
import com.sun.ws.rest.spi.resource.Inject;
import com.sun.ws.rest.spi.resource.Singleton;
@Path( "injectingsingleton" )
@Singleton
public class InjectingSingletonRootResource {
@Inject
private SomeService _someService;
@GET
@ProduceMime("text/plain")
public String getValue() {
return _someService.getSomething();
}
}
Your applicationContext.xml now only has to contain the bean definition for your SomeService…
The instance of SomeService is retrieved from spring by its type, so if there are more than one bean definitions for SomeService the injection is not going to work. If this would be a requirement we should use another annotation that allows to set the bean-name (like e.g. @Resource).
Use @Autowire to autowire your resources with spring-2.0
If you want to get your resource class autowired, you can annotate it with @Autowire, which is provided by the jersey-spring-integration package. Resource classes annotated with @Autowire are created by spring (also spring BeanPostProcessors are applied). The @Autowire also allows to specify the AutowireMode (one of AUTODETECT, BY_NAME, BY_TYPE, CONSTRUCTOR, see AutowireCapableBeanFactory) and whether to perform a dependency check for objects (which is not applicable to autowiring a constructor, thus ignored there).
The example is really simple:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;
import com.sun.ws.rest.spi.resource.Inject;
import com.sun.ws.rest.spi.resource.Singleton;
import com.sun.ws.rest.spring.annotations.Autowire;
@Path( "injectingsingleton" )
@Singleton
@Autowire
public class AutowiringSingletonRootResource {
@Inject
private SomeService _someService;
@GET
@ProduceMime("text/plain")
public String getValue() {
return _someService.getSomething();
}
}
Your applicationContext.xml still has to contain the bean definition for your SomeService. As this AutowiringSingletonRootResource is created by spring it can make use of all spring features, but you don’t have to define each resource in your applicationContext.xml.
Retrieve your subresources from the ResourceContext
If you have your root resource classes created by spring, you’re perhaps happy that you can use features provided by spring like AOP, transaction-handling or caching. But what to do when your root resource must return some sub-resource (sub-resource locator), but this sub-resource shall also be created by spring? Until now the sub-resource was created using new() and therefore it could not be created by spring and therefore was not proxied - no AOP, transaction-handling or caching…
This can now be achieved using the ResourceContext, that can be used to retrieve an instance of some sub-resource:
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import spring.examples.UserService.User;
import com.sun.ws.rest.api.NotFoundException;
import com.sun.ws.rest.api.core.ResourceContext;
import com.sun.ws.rest.spi.resource.Inject;
import com.sun.ws.rest.spi.resource.Singleton;
@Path( "users" )
@Singleton
public class UsersResource {
private final UserService _userService;
@Context
private ResourceContext _resourceContext;
public UsersResource( UserService userService ) {
_userService = userService;
}
@Path( "{username}" )
public UserResource getValue( @PathParam( "username" ) String username ) {
final User user = _userService.findByUsername( username );
if ( user == null ) {
throw new NotFoundException( "User " + username + " not found!" );
}
final UserResource result = _resourceContext.getResource( UserResource.class );
result.setUser( user );
return result;
}
}
In this example, I asume that both the UsersResource and the UserResource are defined in your applicationContext.xml (and the UserService of course also) and therefore created by spring, the ResourceContext is injected by jersey.
Using spring-2.5 features
Just to mention, it’s also possible to use annotation driven bean configuration as it came with spring-2.5. This is an example for a resource class, also notice that both scopes must match:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.sun.ws.rest.spi.resource.PerRequest;
@Path( "injectingsingleton" )
@PerRequest
@Component
@Scope( "prototype" )
public class AnnotatedPerRequestResource {
@Autowired
private SomeService _someService;
@GET
@ProduceMime("text/plain")
public String getValue() {
return _someService.getSomething();
}
}
That spring discovers this resource class, the applicationContext.xml must contain the , as it’s described in the spring documentation.