Skip to main content
Last updated January 25, 2012 10:00, by m_potociar
Feedicon  

Java Dependency Injection (JSR 330) Integration in JAX-RS (Draft)

Dependency injection API currently exposes only a limited API. Following discussion details how each aspect of the injection API can be integrated into JAX-RS.

Supporting JSR 330 Injection Style

The proposal is expose all the existing JAX-RS injection points via the JSR 330 injection style. There are some inconsistencies between JAX-RS injection and DI, that need to be resolved.

JAX-RS Injection overview

JAX-RS injection model recognises multiple injection point types:

Type-based injection points
annotated with @Context, where the injected data are inferred based on the injected (JAX-RS API) type/interface.
Qualified injection points
annotated with one of the @*Param annotations, where the injected data are inferred from the used injection annotation. Once the injected content is resolved the content is then formatted to accommodate specific injection point types. A default value for injection points of this type can be specified via @DefaultValue annotation. This value is applied to the injection point in case the resolved injection content is null.
Implicit request data injection
a specific type of injection point that occurs in HTTP method definitions, where last input argument that is not annotated with any JAX-RS injection annotation is interpreted as a request to inject properly typed request entity, or whole request, provided the parameter type is JAX-RS core.Request

All JAX-RS injection points are directly annotated, implicit request data injection being the only exception to the rule. For further reference it is also important to mention that all JAX-RS injection annotations can be applied to the declarations of

  • fields
  • methods
  • individual method parameters

Additionally, JAX-RS injection points cannot be scoped, and all injected data however relate to the scope of the running HTTP request/response processing. Yet, the actual scope of the injected data is undefined.

JSR 330 Injection overview

All injection points using DI are annotated (directly or indirectly) with @Inject, which can be applied to the declaration of:

  • fields
  • non-abstract methods (that don't declare their own type parameters)
  • at most one constructor in the class

Similarly to JAX-RS, JSR 330 supports multiple injection point types:

Type-based injection points
Injection points that are resolved according to their declared types.
Qualified injection points
Injection points that are resolved according to their declared types AND an arbitrary qualifier annotation(s)

Since @Inject, as you may have already noticed, does not apply to the method parameter declarations, as method parameter injection is declared by annotating the method. Qualifier annotations that denote the qualified injection points however typically apply directly to the fields and method parameters (although the specification does not prohibit the use of qualifier annotations on methods or classes).

The JSR 330 specification does not provide any API for defining injection bindings. Such APIs are currently implementation-specific. However many existing implementations support implicit bindings. Implicit bindings are the ones that are not explicitly defined via the binding API and are resolved at runtime by loading the class of the requested type, instantiating it and potentially injecting the new instance itself.

Scope of the injected instances is by default per-lookup, however JSR 330 provides mechanisms for declaring custom scope annotations that can be attached on the injectable classes in order to modify the scope of injectable instances of such class. By default, @Singleton annotation declaring the per-VM singleton scope is defined by the specification.

Injecting JAX-RS using existing JSR 330 API

While there are obvious differences between JAX-RS and JSR 330 injection models, the support for standard DI in JAX-RS seems reasonably achievable with following updates to the JAX-RS spec:

  • In order to use JSR 330 injection style, the users MUST annotate all field, setter & constructor injection points (directly or indirectly in case of method parameters) with @Inject.
  • JSR 330 @Qualifier annotation MUST be added to the definition of all JAX-RS @*Param annotations.
  • JAX-RS specification MUST enumerate all the implicitly defined JAX-RS specific bindings, including the suported injection point types required qualifiers and scope of the bound injectable instances. This can either be done in javadoc of each injectable interface, but it would be recommended to include these definition in a form of a new appendix accompanying the specification. Here's a short example of such binding definition:
    • Injection point: Named path parameter; Supported IP types: String; Required qualifiers: @PathParam, value specifying the template name of the injected path parameter; Injectable instance scope: JAX-RS request scope

With these changes following table illustrates how a typical JAX-RS resource definition (left) can be rewritten using JSR 330 injection (right):

@Path("resource/{id}")
public class Resource {
    @PathParam("id") String id;
    @Context HttpHeaders headers;

    @GET
    public String get(@QueryParam String language) { ... }

    @PUT
    public String put(String request) { ... }   
}
@Path("resource/{id}")
public class Resource {
    @Inject @PathParam("id") String id;
    @Inject  HttpHeaders headers;

    @GET
    public String get(@QueryParam String language) { ... }

    @PUT
    public String put(String request) { ... }   
}

or, using constructor injection:

@Path("resource/{id}")
public class Resource {
     private final String id;
     private final HttpHeaders headers;


    public Resource(@PathParam("id") String id, @Context HttpHeaders headers) {
        this.id = id; this.headers = headers;
    }

    @GET
    public String get(@QueryParam String language) { ... }

    @PUT
    public String put(String request) { ... }   
}
@Path("resource/{id}")
public class Resource {
     private final String id;
     private final HttpHeaders headers;

    @Inject
    public Resource(@PathParam("id") String id, HttpHeaders headers) {
        this.id = id; this.headers = headers;
    }

    @GET
    public String get(@QueryParam String language) { ... }

    @PUT
    public String put(String request) { ... }   
}

Requests for enhancements to Java Dependency Injection

In order to make Java Depndency Injection story complete in JAX-RS, we propose following enhancements to the existing DI API:

Injector API
To allow easier integration between DI provider and JAX-RS provider, especially for injecting HTTP and sub-resource locator methods with data which is not managed/provided by JAX-RS implementation directly.
Binding API
A standard API for defining custom bindings would further reduce coupling between JAX-RS and DI providers as well as provide a standard way for extending injection support with additional application-specific data types, scopes, qualifiers etc.

Suporting JSR 330 Scopes

As mentioned previously, JAX-RS works with the concept of it's own request and application/singleton scopes, although these scopes are not explicitly defined in the specification. The proposal is that:

  • JAX-RS request and application scopes MUST be formally defined

Optionally, to further extend the explicit support for JAX-RS scopes, we may consider also defining scope annotations for JAX-RS request and application scopes, in which case the JSR 330 @Singleton scope annotation may be considered as the JAX-RS application scope denominator. These scope annotations can be then used to scope the life cycle of all JAX-RS providers.

Due to the deficiencies in the current JSR 330 API, it is proposed that JAX-RS specification does not define any support for custom injection scopes.

Supporting JSR 330 Providers

JAX-RS introduces ContextResolver contract that overlaps in functionality with the Provider defined in JSR 330. In general, JAX-RS context resolvers are just glorified JSR-330 providers. Following support for JSR 330 providers MAY be considered:

  • javax.inject.Provider implementations may be discovered using the existing JAX-RS provider discovery mechanism
  • JAX-RS scope annotations (if defined according to the previous section) may be used to define the scope of the discovered JSR330 providers.
  • Similarly to context resolvers, JSR-330 providers MAY be annotated with @Produces annotation.
 
 
Close
loading
Please Confirm
Close