Jersey 2.x 自定义注入注释与属性

2024-03-04

我正在从 DropWizard 0.7.1 迁移到 0.8.1。这包括从 Jersey 1.x 迁移到 2.x。在我的 使用 Jersey 1.18.1 的实现,我有一个MyProvider(为了简单起见,更改了所有类名)实现InjectableProvider。这 类将创建MyInjectable对象,包含自定义注入注释,MyToken. MyToken包含各种属性 被传递和阅读MyInjectable。最后,在Application类 I 注册一个新实例MyProvider,如下所示。

我已经做了一些研究,但似乎无法完全理解如何在 Jersey 2.x 中重新创建(或替换)这样的场景。

这是当前的 1.18.1 实现:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.PARAMETER, ElementType.FIELD })
    public @interface MyToken {

        // Custom annotation containing various attributes
        boolean someAttribute() default true;
        // ...
    }

    public class MyProvider implements InjectableProvider<MyToken, Parameter> {

        // io.dropwizard.auth.Authenticator
        private final Authenticator<String, MyObject> authenticator;

        public MyProvider(Authenticator<String, MyObject> authenticator) {
            this.authenticator = authenticator;
        }

        @Override
        public ComponentScope getScope() {
            return ComponentScope.PerRequest;
        }

        @Override
        public Injectable<?> getInjectable(ComponentContext ic, MyToken t, Parameter p) {
            return new MyInjectable(authenticator, t.someAttribute());      
        }
    }

    class MyInjectable extends AbstractHttpContextInjectable<MyObject> {
        private final Authenticator<String, Session> authenticator;
        private final boolean someAttribute;

        public MyInjectable(Authenticator<String, MyObject> authenticator, boolean someAttribute) {
            this.authenticator = authenticator;
            this.someAttribute = someAttribute;
            // ... Removed a few paramters for simplicity's sake
        }

        @Override
        public MyObject getValue(HttpContext c) {
            final HttpRequestContext request = c.getRequest();
            // ... Removed code not pertaining to the question
            return myObject;
        }
    }

// Lastly, the register call in the io.dropwizard.Application class
environment.jersey().register(new MyProvider(new MyProviderValidator(someValidator)));

是的,Jersey 在 2.x 中使自定义注入的创建变得更加复杂。对于 Jersey 2.x,您需要了解一些自定义注入的主要组件

  • org.glassfish.hk2.api.Factory https://javaee.github.io/hk2/apidocs/org/glassfish/hk2/api/Factory.html- 创建可注入对象/服务
  • org.glassfish.hk2.api.InjectionResolver https://javaee.github.io/hk2/apidocs/org/glassfish/hk2/api/InjectionResolver.html- 用于为您自己的注释创建注入点。
  • org.glassfish.jersey.server.spi.internal.ValueFactoryProvider http://static.javadoc.io/org.glassfish.jersey.core/jersey-server/2.6/org/glassfish/jersey/server/spi/internal/ValueFactoryProvider.html- 提供参数值注入。

您可以阅读有关自定义注入的更多信息定制注入和生命周期管理 https://jersey.github.io/documentation/latest/ioc.html。该文档的一个缺点是缺乏如何注入参数值的解释。你可以通过简单地实现InjectResolver,您将能够使用自定义注释注入字段,但为了注入方法参数,我们需要ValueFactoryProvider.

幸运的是,我们可以扩展一些抽象类(文档也没有提及),这将使生活变得更轻松。我必须搜寻的源代码org.glassfish.jersey.server.internal.inject package https://github.com/jersey/jersey/tree/master/core-server/src/main/java/org/glassfish/jersey/server/internal/inject花点时间尝试弄清楚这一切。

这是一个完整的示例,可帮助您入门。

Token(可注射物体)

public class Token {
    private final String token;
    public Token(String token) { this.token = token; }
    public String getToken() { return token; }
}

@TokenParam(我们的注入注释)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface TokenParam {
    boolean someAttribute() default true;
}

TokenFactory(实现Factory根据第一个要点,但我们只是扩展AbstractContainerRequestValueFactory。在那里我们可以访问ContainerRequestContext。请注意,所有这些 HK2 组件,我们都可以向其中注入其他依赖项,例如TokenAuthenticator,稍后我们将其绑定到 HK2。

import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;

public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {
    
    private final TokenAuthenticator tokenAuthenticator;
    
    @Inject
    public TokenFactory(TokenAuthenticator tokenAuthenticator) {
        this.tokenAuthenticator = tokenAuthenticator;
    }
    
    @Override
    public Token provide() {
        String auth = getContainerRequest().getHeaderString(HttpHeaders.AUTHORIZATION);
        try {
            if (tokenAuthenticator.authenticate(auth).get() == null) {
                throw new WebApplicationException(Response.Status.FORBIDDEN);
            }
        } catch (AuthenticationException ex) {
            Logger.getLogger(TokenFactory.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return new Token("New Token");
    }  
}

TokenParamInjectionResolver(实现了InjectResolver每个要点第二点。我只是简单地扩展ParamInjectionResolver。如果您对幕后发生的事情感兴趣,您可以在我链接到的源代码中找到该类)

import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;

public class TokenParamInjectionResolver extends ParamInjectionResolver {
    public TokenParamInjectionResolver() {
        super(TokenFactoryProvider.class);
    }
}

TokenFactoryProvider(实现了ValueFactoryProvider根据第三个要点。我只是简单地扩展AbstractValueFactoryProvider。同样,您可以查看源代码以获取幕后详细信息)

import javax.inject.Inject;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.model.Parameter;

public class TokenFactoryProvider extends AbstractValueFactoryProvider {
    
    private final TokenFactory tokenFactory;
    
    @Inject
    public TokenFactoryProvider(
            final MultivaluedParameterExtractorProvider extractorProvider,
            ServiceLocator locator,
            TokenFactory tokenFactory) {
        
        super(extractorProvider, locator, Parameter.Source.UNKNOWN);
        this.tokenFactory = tokenFactory;
    }

    @Override
    protected Factory<?> createValueFactory(Parameter parameter) {
         Class<?> paramType = parameter.getRawType();
         TokenParam annotation = parameter.getAnnotation(TokenParam.class);
         if (annotation != null && paramType.isAssignableFrom(Token.class)) {
             return tokenFactory;
         }
         return null;
    }
}

TokenFeature(这里我们绑定了上面看到的所有组件,甚至是TokenAuthentictor,我省略了,但如果你常用的 DropwizardAuthenticator。我还使用了Feature。我倾向于这样做来包装自定义功能的组件。您也可以在此处决定所有范围。只需注意一些组件需要位于Singleton scope)

import javax.inject.Singleton;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;

public class TokenFeature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(new AbstractBinder(){
            @Override
            public void configure() {
                bind(TokenAuthenticator.class)
                        .to(TokenAuthenticator.class)
                        .in(Singleton.class);
                bind(TokenFactory.class).to(TokenFactory.class)
                        .in(Singleton.class);
                bind(TokenFactoryProvider.class)
                        .to(ValueFactoryProvider.class)
                        .in(Singleton.class);
                bind(TokenParamInjectionResolver.class)
                        .to(new TypeLiteral<InjectionResolver<TokenParam>>(){})
                        .in(Singleton.class);
            }
        });
        return true;
    } 
}

最后简单地注册该功能

register(TokenFeature.class);

现在您应该能够注入Token with @TokenParam,以及您通常的实体机构(如果我们不实现的话,这是不可能的ValueFactoryProvider

@POST
@Consumes(MediaType.APPLICATION_JSON)
public String postToken(@TokenParam Token token, User user) {
    
}

UPDATE

对于您的特定用例来说,这是一个半@$$示例。更好的方法可能是在你的Factory类并创建一个新的TokenFactory带有一些参数(也许你从注释中得到. For example, in the TokenFactory`你可以有类似的东西

public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {

    public TokenFactory clone(boolean someAttribute) {
        return new TokenFactory(authenticator, someAttribute);
    }

In the TokenFactoryProvider ine createValueFactory方法,然后调用克隆方法

TokenParam annotation = parameter.getAnnotation(TokenParam.class);

if (annotation != null && paramType.isAssignableFrom(Token.class)) {
    return tokenFactory.clone(annotation.someAttribute());
}

或者你实际上可以create方法内部的工厂。你有选择。

UPDATE 2

See Also

  • jersey 2 基于 Http 请求的上下文注入,无需单例 https://stackoverflow.com/q/34747574/2587435

UPDATE 3

从 Jersey 2.26 开始,依赖注入发生了变化。你会想看看这个帖子 https://stackoverflow.com/q/50975973/2587435例如,代码在实现同一注入时发生了怎样的变化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Jersey 2.x 自定义注入注释与属性 的相关文章

随机推荐

  • 我怎样才能改变kivy窗口位置

    有没有其他方法可以在不使用以下内容的情况下更改窗口位置 from kivy config import Config Config set graphics position custom Config set graphics left
  • 使用 XAMPP Server Linux (Ubuntu) 在 Laravel 4 上迁移时找不到驱动程序

    我已经安装了 Laravel 4 1 一切正常 但是当我尝试使用迁移时 它会抛出一个 PDO 异常 PDO异常 找不到驱动程序 我在 StackOverFlow 上找到了很多答案 但不幸的是 没有一个能解决我的问题 我尝试启用 pdo 扩展
  • 将 NaN 值替换为每列的特定值

    我有一个包含值以及一些 NaN 的数据框 现在我有了列的平均值 我想将特定列的平均值插入到 NaN 值中 例如 ColA 和 ColB 的 NaN 被替换为我有的平均值 https i stack imgur com vAr2p png 我
  • Java 11 迁移:可从多个模块访问包问题

    我正在将 Java 8 项目迁移到 Java 11 使用的 IDE 是 Eclipse 2020 09 我还没有模块化该项目 对于以下进口 import org w3c dom Document import org w3c dom Ele
  • 生成 parquet 文件 - R 和 Python 之间的差异

    我们已经生成了一个parquet文件输入Dask Python 并与Drill R 使用Sergeant包 我们注意到几个问题 的格式Dask i e fastparquet has a metadata and a common meta
  • 将由空格分隔的字符串中的单词分配给变量? C++ [qt] [重复]

    这个问题在这里已经有答案了 我只是想知道将空格分隔的字符串中的单词分配给变量的技术术语叫什么 这样我就可以查找它的教程 任何术语 链接或解释将不胜感激 你可以这样做 QString lineText some sample words QS
  • 在 Sitecore MVC 中的视图中获取媒体 url

    我有一个存储在媒体库中的 mp3 文件 它在项目的媒体字段中被引用 我可以通过在 Web 表单或类中执行 Sitecore Resources Media MediaManager GetMediaURL 来获取 URL 但是如何在 MVC
  • 在 smartGWT 中打开/保存文件

    我已经实现了 RPCService RPCServiceAsync 和 RPCServieImpl 单击按钮后 将调用服务器端的服务 它将从数据库中获取数据并创建文件 创建文件后 我需要在客户端打开该文件 并需要提示一个包含打开 保存选项的
  • ServiceStack - 依赖关系似乎没有被注入?

    我有以下存储库类 public class Repository
  • 微服务架构中的数据库位置

    我们有一个整体应用程序 现在正在使用容器将其转换为微服务架构 我们的微服务是stateful 即他们需要从数据库插入 检索数据 根据微服务架构 每个微服务应该有自己的数据 即我们案例中的数据库 我的问题是where应该部署每个微服务的数据库
  • 实体框架数据库优先与 Oracle 数据库

    我正在开发一个 ASP NET WebForms 应用程序 其中实体框架数据库优先与 SQL Server 连接 并且我想将相同的实体数据模型与 Oracle 数据库连接 我的担忧是 如何在我的开发机器上安装 Oracle 数据库引擎进行测
  • 使用用户定义的运行时属性的 UIView Shadow

    当我使用 用户定义的运行时属性 时 我很难显示阴影 如果我使用代码 它似乎工作完全正常 如下所示 func formatView view UIView cornerRadius Bool if cornerRadius view laye
  • Python Selenium Javascript链接点击无法执行

    我将 Selenium for Python 与 PhantomJS Ghost 驱动程序一起使用 以便单击 href 中包含 Javascript 的链接 例如来自this https structuredginniemaes ginni
  • MongoDB 根据 _id 统计每分钟新文档数

    我想创建每分钟存储多少新文档的统计数据 由于具有标准 ObjectID 的 id 字段已经包含文档创建的时间戳 我认为应该可以以某种方式使用它 在 Stackoverflow 上 我发现了以下映射归约代码 可以在有用于创建数据的专用字段时完
  • 从具有特定窗口坐标的命令行启动 Google Chrome

    我正在尝试找到一个 shell 命令 该命令将使用特定的 x 和 y 坐标打开 Google Chrome 以便我可以在窗口打开时设置窗口的位置 是否可以使用命令行参数来执行此操作 我需要修改以下命令才能实现此目的 google chrom
  • python中不均匀的子图

    在 python 中创建 3 3 子图矩阵的最佳方法是什么 第一列包含 3 个子图 第二列包含 3 个子图 第三列包含2 个次要情节 最后两个子图应具有相同的大小 这意味着它们将在其他两列的中间图的中间相遇 我尝试使用 gridspec 来
  • 当应用程序未处于焦点或位于另一个选项卡中时,WaitForSeconds 停止工作

    我用 Unity 制作了 WebGL 游戏 不需要每个帧都进行一些计算 因此我将它们放在协程中 但当游戏在后台运行时 协程的工作速度会比平时慢 并且会额外等待 5 10 秒 例如 士兵跑向我 我向他们发射子弹以杀死他们 当他们进入射程时 他
  • 引导程序上的下拉子菜单不起作用

    我只是想问为什么引导程序上的下拉子菜单不起作用 我只是按照此链接中的说明操作 http getbootstrap com 2 3 2 components html http getbootstrap com 2 3 2 component
  • 从 shell 脚本将密码输入 openssl 命令

    我正在尝试将 p12 从 shell 脚本转换为 pem 无需任何用户输入 我可以将密码作为脚本中的变量 所以当我打电话时 openssl pkcs12 in p12 out cert pem nodes 终端打印 输入导入密码 并等待输入
  • Jersey 2.x 自定义注入注释与属性

    我正在从 DropWizard 0 7 1 迁移到 0 8 1 这包括从 Jersey 1 x 迁移到 2 x 在我的 使用 Jersey 1 18 1 的实现 我有一个MyProvider 为了简单起见 更改了所有类名 实现Injecta