dependency-injection - 泽西 @Context 范围

标签 dependency-injection jersey jax-rs

我很难理解 Jersey 的注入(inject)机制。 JAX-RS 规范 (http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005) 指出,在 Application 子类、根资源类和提供者中可以通过 @Context 进行注入(inject)。

我现在有一个在启动时实例化的类,并且有一个在每个请求上调用的方法。在方法内部,我需要访问当前的 UriInfo 对象。问题是,这个方法不是从我的代码中调用的。所以我不能直接将 UriInfo 传递给方法。

我实际上想做这样的事情:

public class MyClass implements ThirdPartyInterface {

    // not possible because class is no Application subclass, root resource class or provider
    @Context
    private UriInfo uriInfo;

    public void methodCallebByThirdPartyCode() {
        Uri requestUri = uriInfo.getRequestUri();

        // do something
    }
}

我试过这个。显然没有成功:

public class MyClass implements ThirdPartyInterface {

    private UriInfo uriInfo;

    public MyClass(UriInfo uriInfo) {
        this.uriInfo = uriInfo;
    }

    public void methodCallebByThirdPartyCode() {
        Uri requestUri = uriInfo.getRequestUri();

        // do something
    }
}

@Provider
@Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {

    @Context
    private UriInfo uriInfo;

    private MyClass myClass;

    private ThirdPartyClass thirdPartyClass;

    public MyBodyWriter() {
        // uriInfo is null at this time :(
        myClass = new MyClass(uriInfo);

        thirdPartyClass = new ThirdPartyClass();
        thirdPartyClass.register(myClass);
    }

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
        // execute() calls MyClass#methodCallebByThirdPartyCode()
        thirdPartyClass.execute();
    }
}

我能想到的唯一解决方法是这个。我不认为它很干净:

public class MyClass implements ThirdPartyInterface {

    private UriInfo uriInfo;

    public void setUriInfo(final UriInfo uriInfo) {
        this.uriInfo = uriInfo;
    }

    public void methodCallebByThirdPartyCode() {
        Uri requestUri = uriInfo.getRequestUri();

        // do something
    }
}

@Provider
@Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {

    @Context
    private UriInfo uriInfo;

    private MyClass myClass;

    private ThirdPartyClass thirdPartyClass;

    public MyBodyWriter() {
        myClass = new MyClass();

        thirdPartyClass = new ThirdPartyClass();
        thirdPartyClass.register(myClass);
    }

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
        myClass.setUriInfo(uriInfo);

        // execute() calls MyClass#methodCallebByThirdPartyCode()
        thirdPartyClass.execute();

        myClass.setUriInfo(null);
    }
}

我希望有更好的解决方案,但也许我完全走错了路。

谢谢!

最佳答案

迟到的答案,但一个很好的问题......所以让我们走吧:

您可以使用 org.glassfish.hk2.api.Factoryjavax.inject.Provider用于注入(inject)。我不知道这是从哪个版本可用的,所以也许你必须升级你的 Jersey 版本。对于以下示例,我使用了 jersey 2.12 .

首先,您必须为您的 MyClass 实现和注册/绑定(bind)工厂。 :

我的类工厂:

import javax.inject.Inject;
import javax.ws.rs.core.UriInfo;
import org.glassfish.hk2.api.Factory;
// ...

public class MyClassFactory implements Factory<MyClass> {

    private final UriInfo uriInfo;

    // we will bind MyClassFactory per lookup later, so 
    // the constructor will be called everytime we need the factory
    // meaning, uriInfo is also per lookup

    @Inject
    public MyClassFactory(final UriInfo uriInfo) {
        this.uriInfo = uriInfo;
    }

    @Override
    public MyClass provide() {
        return new MyClass(uriInfo) 
    }

    @Override
    public void dispose(UriInfo uriInfo) {
        // ignore 
    }

}

通过 ResourceConfig 注册:

import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
// ...

public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {
        register(new AbstractBinder() {
            @Override
            protected void configure() {
                bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class);
                // ... bind additional factories here
            }
        });
        // ...
    }

}

现在您可以在每次查找时将 MyClass 注入(inject)到提供程序、资源等。
但注意:Afaig 有两种方法,只有一种方法最终适用于提供商......

import javax.inject.Inject;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
// ...

@Provider
@Produces("application/foo-bar")
public class MyBodyWriter implements MessageBodyWriter<MyView> {

    // first approache - don't do it!
    // will only injected once, cause MyBodyWriter is only instantiated once
    @Inject
    private MyClass myClass;

    // second approache - works fine!
    private final javax.inject.Provider<MyClass> provider;

    // MyBodyWriter instantiate once
    // get an inject provider here
    @Inject
    public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) {
        this.provider = myClassProvider;
    }

    @Override
    public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) {
        return t == MyView.class;
    }

    @Override
    public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) {
        // deprecated by JAX-RS 2.0 and ignored by Jersey runtime
        return 0;
    }

    @Override
    public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException {

        // attention: its not per lookup !!!
        MyClass myClassDirectInjected = myClass;
        System.out.println(myClassDirectInjected); // same instance everytime

        // but this is ;)
        MyClass myClassFromProvider = provider.get();
        System.out.println(myClassFromProvider); // it's a new instance everytime

        // ...
    }

}

希望这在某种程度上有所帮助。

关于dependency-injection - 泽西 @Context 范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16424165/

相关文章:

c# - 将 appsettings 配置 DI 添加到 HostBuilder 控制台应用程序

java - Dagger 2 Android - 将()依赖项注入(inject) ViewModel 与具有依赖项引用的应用程序

jboss as 7 + Jersey - ClassNotFound com.sun.jersey.server.impl.container.servlet.Include

java - 使用 Jersey 对查询数组参数进行排序

java - 为什么这个 jersey-ejb 示例失败了?

java - 提供链接而不是序列化集合

javascript - 如何在 Aurelia Typescript 中创建通用服务

php - Symfony:如何获取所有服务及其各自的类

java - 使用单个 JsonView 过滤应用程序的每个 JAX-RS 资源

java - 带有嵌入式服务器的 JAX-RS