11

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

@GET
public Response getTest(@Context MyObject myObject) { // this works

但是,对于 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");
}

有没有办法定义 HK2 工厂何时运行,以便在过滤器运行调用它?如果不是,那么解决方法是将 MyObject 定义为一个接口,并定义一个额外的实现,该实现在其构造函数中采用 ContainerRequestContext;任何实际使用该实例的尝试都会懒惰地委托给在 ContainerRequestContext 的属性上设置的实现(大概在过滤器运行之前您不会实际使用该实例——此时将设置该属性)。

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

4

1 回答 1

12

奇怪的是,它只适用于我@PreMatching的过滤器(它限制了对某些您可能需要或可能不需要的东西的访问)。不太清楚幕后发生了什么,导致没有它就无法工作:-(。下面是使用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);
    }
}

更新

无需将其制成@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();
    }
}
于 2015-06-16T15:29:09.603 回答