当前位置: 首页 > 知识库问答 >
问题:

泽西岛@Context范围

梁楷
2023-03-14

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

我现在有一个在启动时实例化的类,并且有一个在每次请求时调用的方法。在方法内部,我需要访问当前的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();
    }
}

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

java prettyprint-override">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);
    }
}

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

谢谢

共有1个答案

强志学
2023-03-14

回答晚了,但是一个好问题...所以我们走吧:

您可以使用org.glassfish.hk2.api.Factoryjavax.inject.Provider进行注射。我不知道从哪个版本开始可用,所以也许你必须升级你的jersery版本。对于以下示例,我使用了jersey 2.12

首先,您必须为 MyClass 实现并注册/绑定工厂:

我的类工厂:

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 
    }

}

通过资源配置注册:

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到提供者,资源等,
但是请注意: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

        // ...
    }

}

希望这能有所帮助。

 类似资料:
  • 我目前使用的是球衣 我现在要做的是设置泽西,这样当查询参数进来时(比如缩进),我可以告诉Jackson以“更漂亮的格式,也就是缩进”序列化JSON。您可以通过使用SerializationConfig.Feature.INDENT_OUTPUT配置JSON映射器来轻松地告诉Jackson这样做。 问题是,我如何在每个请求的基础上获取一个queryparam并使用它来修改Jackson的输出?

  • 问题内容: 我有一个Jersey REST API,并且正在使用来处理授权。我还在所有端点上使用,以便我的API可以处理数千个并发请求。 我的授权过滤器命中了一个远程服务,但是当运行过滤器时,Jersey尚未将当前线程添加到它的内部,因此我完全失去了异步的好处。 我可以告诉Jersey我希望这是异步的吗? 这是一个示例资源: 更新 刚刚收到泽西队球员的回音,从2.7开始,这是不可能的。只有资源方法

  • 我正在泽西岛2中编写一个RESTful网络服务。我想支持响应的 Gzip 编码。按照这个答案,我在我的类中启用了。 在我的资源类中,我返回一个对象。 现在,当我点击这个api时,我得到一个响应,但响应头不包含头,而是包含 header。 请求: 响应: 响应中没有 标头,也没有任何标头。 我在上使用。 我还遗漏了其他配置吗?我如何获得这两个头并得到gzip压缩的响应,而不是接收分块的响应? 编辑:

  • 问题内容: 我在Glassfish上有一个工作正常的JavaEE 6 Web应用程序,该应用程序已经具有JSF前端并具有其身份验证机制((使用基于CDI和注释的安全性),因此有一个登录屏幕,用户输入用户名密码,按登录按钮和Java EE身份验证过程开始。 现在,我想“还将”我的一些服务类公开为REST服务(我可能会使用Jersey),因此也可以从移动设备访问它。但是让我担心的是登录部分。 我将使用

  • 我有一个问题,让昂首阔步的博士一代工作。 以下是我使用的专家: 我在ResourceConfig类中添加了swagger ressources: 然后我声明了一个 servlet 来配置 Swagger: 通过上面的配置,可以使用swagger servlet:如果我浏览到api文档url,我会得到以下信息: 但是,如果我使用如下所示的真实配置更改 swagger servlet 配置中的 api

  • 问题内容: 我的问题是:为什么在创建部署在某种servlet容器(如jetty或tomcat)上的应用程序时执行JavaSE应用程序和ServletModule时需要创建AbstractModule?它们之间有什么区别? 我需要将Jersey与Guice集成在一起。是否需要注册Guice的存在才能以某种方式使用它?我是否可以仅启用注入并在我想要的任何地方(常规类,过滤器,处理程序,服务,DAO等)