如何将对象注入球衣请求上下文?

我有这样一个场景,我想编写一个过滤器,我希望这个过滤器将一些对象插入到当前请求中并将其传递,以便当资源类收到请求时,它可以使用该对象。

筛选器类

@Override
public void filter(ContainerRequestContext request) throws IOException {
    MyObject obj = new MyObject();
    // Inject MyObject to request which I dont know how
}

资源类

@PUT @Consumes("application/json")
@Path("/")
public String create(
        JSONParam sample,
        @Context MyObject obj) {

    System.out.println(obj.getName());

    return "";
}

答案 1

你可以只使用.然后只需注射ContainterRequestContext.setProperty(String, Object)ContainerRequestContext

@Override
public void filter(ContainerRequestContext crc) throws IOException {
    MyObject obj = new MyObject();
    crc.setProperty("myObject", myObject);
}

@POST
public Response getResponse(@Context ContainerRequestContext crc) {
    return Response.ok(crc.getProperty("myObject")).build();
}

直接注入的另一种选择是使用泽西岛2提供的HK2功能。MyObject

创建一个注入的工厂并返回 .例如ContainerRequestContextMyObject

import javax.inject.Inject;
import javax.ws.rs.container.ContainerRequestContext;
import jetty.plugin.test.domain.MyObject;
import org.glassfish.hk2.api.Factory;

public class MyObjectFactory implements Factory<MyObject> {
    
    private final ContainerRequestContext context;
    
    @Inject
    public MyObjectFactory(ContainerRequestContext context) {
        this.context = context;
    }

    @Override
    public MyObject provide() {
        return (MyObject)context.getProperty("myObject");
    }

    @Override
    public void dispose(MyObject t) {}  
}

然后,您需要绑定工厂:

public class InjectApplication extends ResourceConfig {
    
    public InjectApplication() {
        ...
        register(new AbstractBinder(){
            @Override
            protected void configure() {
                bindFactory(MyObjectFactory.class)
                        .to(MyObject.class)
                        .in(RequestScoped.class);
            } 
        });
    }
}

使用与上面的筛选器示例中相同的属性设置,然后只需注入MyObject@Context

@GET
public Response getTest(@Context MyObject myObject) {
    return Response.ok(myObject.getMessage()).build();
}


更新

有关实现的问题,请参阅此问题。

另请参阅:


答案 2

我有一个解决方案,它不需要DI容器,但仍然提供了大部分好处。

有两个部分。第一个是如何将实例放入@Context注入机制,而不是在 ApplicationConfig 对象中提供类。

以下是执行此操作的技术:

private static class CustomContextResteasyBootstrap extends org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap{
    private final Map<Class<?>, Object> additionalContextObjects = new HashMap<Class<?>, Object>();

    public <E> CustomContextResteasyBootstrap addContextObject(Class<? super E> clazz, E obj){
        additionalContextObjects.put(clazz, obj);
        return this;
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        super.contextInitialized(event);
        deployment.getDispatcher().getDefaultContextObjects().putAll(additionalContextObjects);
    }

}

你这样使用它:

        webAppContext.addEventListener(
                new CustomContextResteasyBootstrap()
                    .addContextObject(MyCustom.class, myCustom)
                    .addContextObject(AnotherCustom.class, anotherCustom)
                    // additional objects you wish to inject into the REST context here
            );

现在,您可以将这些类与@Context注释一起使用:

@GET
public MyCustom echoService(@Context MyCustom custom) {
    return custom;
}

谜题的下一部分是如何提供每个请求的上下文对象。为此,请在 jax-rs 调用层次结构顶部附近的某个位置添加以下代码(基本上,在此行下方调用的任何内容都将访问上下文对象):

    ResteasyProviderFactory.pushContext(MyContextSpecific.class, new MyContextSpecific());

然后,您可以通过注入低于该级别的任意位置来引用此内容:

@GET
public String contextSpecificEchoService(@Context MyContextSpecific contextSpecific) {
    return custom.toString();
}

这是穷人的DI,但它对于嵌入式休息服务器非常有效。


推荐