java - HK2 Factory invocada antes del filtro Jersey cuando se utiliza @Context para la creación de setter/campo/constructor
dependency-injection jersey-2.0 (1)
He podido inyectar en mi jersey de recursos de un filtro de acuerdo con Cómo inyectar un objeto en el contexto de solicitud jersey? . Esto me permite inyectar con éxito en un parámetro de método:
@GET
public Response getTest(@Context MyObject myObject) { // this works
Sin embargo, para la inyección de setter / campo / constructor, se invoca HK2 Factory antes del filtro jersey, lo que significa que el método provide () devuelve null:
@Override
public MyObject provide() {
// returns null because the filter has not yet run,
// and the property has not yet been set
return (MyObject)context.getProperty("myObject");
}
¿Hay alguna manera de definir cuándo se ejecutará HK2 Factory para que se invoque después de que se ejecute el filtro? De lo contrario, la solución es definir MyObject como una interfaz y definir una implementación adicional que tome un ContainerRequestContext en su constructor; cualquier intento de usar realmente la instancia delegaría lentamente en la implementación que se establece en la propiedad ContainerRequestContext (presumiblemente, no usaría la instancia hasta después de que se haya ejecutado el filtro, en cuyo punto se establecería la propiedad).
Pero me gustaría entender si es posible retrasar el punto en el que se ejecuta HK2 Factory para que se ejecute después del filtro (ya se ejecuta después del filtro en el caso de la inyección de parámetros de método). Si no es posible, entonces me gustaría entender si hay una razón fundamental por la cual.
Curiosamente, solo funciona para mí con @PreMatching
en el filtro (lo que limita el acceso a algunas cosas que puede o no necesitar). No estoy seguro de lo que está sucediendo bajo el capó, que hace que no funcione sin él :-(. A continuación se muestra una prueba completa utilizando Jersey Test Framework .
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
public class FilterInjectionTest extends JerseyTest {
private static final String MESSAGE = "Inject OK";
private static final String OBJ_PROP = "myObject";
public static class MyObject {
private final String value;
public MyObject(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@PreMatching
@Provider
public static class MyObjectFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext context) throws IOException {
MyObject obj = new MyObject(MESSAGE);
context.setProperty(OBJ_PROP, obj);
}
}
public static class MyObjectFactory
extends AbstractContainerRequestValueFactory<MyObject> {
@Override
@RequestScoped
public MyObject provide() {
return (MyObject) getContainerRequest().getProperty(OBJ_PROP);
}
@Override
public void dispose(MyObject t) {
}
}
@Path("method-param")
public static class MethodParamResource {
@GET
public String getResponse(@Context MyObject myObject) {
return myObject.getValue();
}
}
@Path("constructor")
public static class ConstructorResource {
private final MyObject myObject;
@Inject
public ConstructorResource(@Context MyObject myObject) {
this.myObject = myObject;
}
@GET
public String getResponse() {
return myObject.getValue();
}
}
@Path("field")
public static class FieldResource {
@Inject
private MyObject myObject;
@GET
public String getResponse() {
return myObject.getValue();
}
}
@Override
public Application configure() {
ResourceConfig config = new ResourceConfig();
config.register(MethodParamResource.class);
config.register(MyObjectFilter.class);
config.register(ConstructorResource.class);
config.register(FieldResource.class);
config.register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(MyObjectFactory.class)
.to(MyObject.class).in(Singleton.class);
}
});
return config;
}
@Test
public void methoParamInjectionOk() {
String response = target("method-param").request().get(String.class);
Assert.assertEquals(MESSAGE, response);
System.out.println(response);
}
@Test
public void costructorInjectionOk() {
String response = target("constructor").request().get(String.class);
Assert.assertEquals(MESSAGE, response);
System.out.println(response);
}
@Test
public void fieldInjectionOk() {
String response = target("field").request().get(String.class);
Assert.assertEquals(MESSAGE, response);
System.out.println(response);
}
}
ACTUALIZAR
La solución, sin tener que convertirlo en un filtro @PreMatching
, es inyectar con javax.inject.Provider
. Esto le permitirá recuperar el objeto de forma perezosa. Supongo que lo que sucede con el constructor y la inyección de campo es que justo después de hacer coincidir la clase de recursos, se crea e inyecta inmediatamente. Debido a que aún no se ha llamado al filtro, no hay ningún objeto para la fábrica. Funciona para la inyección de método, porque es como cualquier otra llamada a método. El objeto se le pasa cuando se llama al método. A continuación se muestra el ejemplo con el javax.inject.Provider
@Path("constructor")
public static class ConstructorResource {
private final javax.inject.Provider<MyObject> myObjectProvider;
@Inject
public ConstructorResource(javax.inject.Provider<MyObject> myObjectProvider) {
this.myObjectProvider = myObjectProvider;
}
@GET
public String getResponse() {
return myObjectProvider.get().getValue();
}
}
@Path("field")
public static class FieldResource {
@Inject
private javax.inject.Provider<MyObject> myObjectProvider;;
@GET
public String getResponse() {
return myObjectProvider.get().getValue();
}
}