HK2工厂在Jersey过滤器之前调用@Context用于setter / field / constructor注入 [英] HK2 Factory invoked prior to Jersey filter when @Context is used for setter/field/constructor injection

查看:216
本文介绍了HK2工厂在Jersey过滤器之前调用@Context用于setter / field / constructor注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据如何将对象注入针织衫请求上下文中,我可以从过滤器注入我的泽西资源。。这使我能够成功地注入一个方法参数:

  @GET 
public Response getTest(@Context MyObject myObject) // //这个工程

然而,对于setter / field / constructor注入,HK2工厂被调用 before 球衣过滤器,这意味着provide()方法返回null:

  @Override 
public MyObject provide(){
//返回null,因为该过滤器尚未运行,
//并且该属性尚未设置
return(MyObject)context.getProperty( myObject的);
}

有没有办法定义HK2工厂何时运行,过滤器运行后调用 如果没有,则解决方法是将MyObject定义为接口,并定义在其构造函数中使用ContainerRequestContext的附加实现;实际使用实例的任何尝试都会懒洋洋地委托给ContainerRequestContext的属性设置的实现(假定您将不会在过滤器运行之后实际使用该实例 - 此时该属性将被设置)。 p>

但是,我想了解是否可以延迟HK2 Factory运行的点,以便在过滤器之后运行(已经在过滤器之后运行)的方法参数注入)。如果不可能,那么我想知道是否有根本原因。

解决方案

奇怪的是它只适用于我在过滤器上使用 @PreMatching (这限制了您可能需要或可能不需要的某些内容的访问权限)。不太确定发动机内部发生了什么,这导致它不能在没有它的情况下工作:-(以下是使用泽西测试框架

  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(){
返回值;
}
}

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


I've been able to inject into my jersey resource from a filter as per How to inject an object into jersey request context?. This allows me to successfully inject into a method parameter:

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

However, for setter/field/constructor injection, the HK2 Factory is invoked before the jersey filter, which means the provide() method returns 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");
}

Is there a way to define when the HK2 Factory will run so that it is invoke after the filter runs? If not, then the workaround is to define MyObject as an interface and define an additional implementation that takes a ContainerRequestContext in its constructor; any attempt to actually use the instance would then lazily delegate to the implementation that gets set on the ContainerRequestContext's property (presumably you wouldn't actually use the instance until after the filter runs -- at which point the property would be set).

But I would like to understand if it is possible to delay the point at which the HK2 Factory runs so that it runs after the filter (it already runs after the filter in the case of method parameter injection). If it is not possible, then I would like to understand if there is a fundamental reason why.

解决方案

Oddly it only works for me with @PreMatching on the filter (which limits access to some things you may or may not need). Not quite sure what's going on under the hood, that cause it not to work without it :-(. Below is a complete test using 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);
    }
}


UPDATE

The solution, without having to make it a @PreMatching filter, is to inject with javax.inject.Provider. This will allow you to lazily retrieve the object. I guess what happens with the constructor and field injection is that right after matching the resource class, it it immediately created and injected. Because the filter hasn't been called yet, there is no object for the factory. It works for the method injection, because it is just like any other method call. The object is passed to it when the method is called. Below is the example with the 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();
    }
}

这篇关于HK2工厂在Jersey过滤器之前调用@Context用于setter / field / constructor注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆