Как вставить объект в контекст запроса трикотажа?

У меня есть этот сценарий, где я хочу написать фильтр, и я хочу, чтобы этот фильтр вставлял какой-то объект в текущий запрос и передавал его, чтобы при получении класса ресурса запрос он мог использовать этот объект.

Класс фильтра

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

Другим вариантом для непосредственного ввода MyObject является использование предложений HK2 Jersey 2.

Создайте factory, введите ContainerRequestContext и верните MyObject. Например

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) {}  
}

Затем вам необходимо привязать factory:

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


UPDATE

См. этот вопрос для проблемы с этой реализацией.

См. также:

Ответ 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();
}

Это плохой человек, но он отлично работает для встроенных серверов отдыха.