当@Context用于setter/field/constructor注入时,在Jersey过滤器之前调用HK2 Factory

rnd*_*stn 9 java dependency-injection jersey-2.0 hk2

我已经能够根据如何将对象注入到球衣请求上下文中从过滤器注入我的球衣资源.这允许我成功注入方法参数:

@GET
public Response getTest(@Context MyObject myObject) { // this works
Run Code Online (Sandbox Code Playgroud)

但是,对于setter/field/constructor注入,HK2 Factory 在jersey过滤器之前调用,这意味着provide()方法返回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");
}
Run Code Online (Sandbox Code Playgroud)

有没有办法定义何时运行HK2 Factory以便在过滤器运行调用它?如果没有,则解决方法是将MyObject定义为接口,并定义在其构造函数中采用ContainerRequestContext的其他实现; 任何实际使用该实例的尝试都将懒惰地委托给在ContainerRequestContext属性上设置的实现(可能在过滤器运行之前你不会实际使用该实例 - 此时将设置该属性).

但我想了解是否有可能延迟HK2工厂运行的点,使其在过滤器之后运行(在方法参数注入的情况下,它已在过滤器之后运行).如果不可能,那么我想了解是否存在根本原因.

Pau*_*tha 10

奇怪的是,它只适用于我@PreMatching的过滤器(限制访问您可能需要或可能不需要的一些东西).不太确定引擎盖下发生了什么,如果没有它就不能工作:-(.以下是使用Jersey测试框架的完整测试.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

解决方案,无需使其成为@PreMatching过滤器,是注入javax.inject.Provider.这将允许您懒惰地检索对象.我想构造函数和字段注入会在匹配资源类之后发生,它会立即创建并注入.由于尚未调用过滤器,因此工厂没有对象.它适用于方法注入,因为它就像任何其他方法调用一样.调用该方法时,该对象将传递给它.下面是一个例子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();
    }
}
Run Code Online (Sandbox Code Playgroud)