java dependency-injection jersey-2.0 hk2

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(); } }