使用 javax.ws.rs.core.Application 实现通用 JAX-RS Web 服务

Posted

技术标签:

【中文标题】使用 javax.ws.rs.core.Application 实现通用 JAX-RS Web 服务【英文标题】:Implementing generic JAX-RS web service using javax.ws.rs.core.Application 【发布时间】:2016-02-13 06:58:12 【问题描述】:

启用 Servlet 3.0 的容器允许我们跳过 web.xml servlet 配置,并在您扩展 javax.ws.rs.core.Application、使用 @ApplicationPath 注释它并且不覆盖 getClasses() 方法后自动扫描您的代码以查找资源和提供程序。 (希望我没看错:\)

目前我正在使用 Jersey 实现并使用 @RolesAllowed 注释保护资源方法。为此,我需要注册 org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature Provider 类,但是,我知道这样做的唯一方法是:

    在我的Application 类的getClasses() 方法中注册该类(我认为这会导致Servlet 3.0 容器不自动扫描)

    继续使用 web.xml Jersey servlet 设置和

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature</param-value>
    </init-param>
    

现在这个问题背后的背景是我可能不得不切换到使用 RESTeasy,如果我使用选项 1,它会在代码中添加 Jersey 依赖项,并且代码不再是通用的。

如何编写代码以使用安全注释,同时维护可部署到另一个 Servlet 3.0 JAX-RS 实现的通用 JAX-RS 代码?

【问题讨论】:

【参考方案1】:

一种选择是使用javax.ws.rs.core.Feature(一种JAX-RS 标准类)。您可以在那里注册任何组件,然后使用@Provider 注释该类,它将像任何其他@Provider@Path 注释类一样被拾取

@Provider
public class MyFeature implements Feature 
    @Overrride
    public boolean configure(FeatureContext context) 
        context.register(RolesAllowedDynamicFeature.class);
    

请注意,由于您使用的是 Jersey 功能,因此您的应用不再独立于实现,因此您不妨一直使用 Jersey。一方面,Jersey 不建议扫描类路径,这是做你正在做的事情的影响。相反,Jersey 有一种机制,允许您递归地扫描包(及其子包)。所以你可以这样做

@ApplicationPath("..")
public class AppConfig extends ResourceConfig 
    public AppConfig() 
        packages("the.packages.to.scan");
        register(RolesAllowedDynamicFeature.class);
    

注意ResourceConfigApplication 的子类

另请参阅:

When to Use JAX-RS Class-path Scanning Mechanism Sevlet Based Deployment - Servlet 3.x Container

注意:

如果您想坚持类路径扫描机制,并希望保持项目独立于任何 Jersey 依赖项,您还可以在 Application 类中覆盖 Map&lt;String, Object&gt; getProperties()。在返回的Map 中,您可以添加您本来会在 web.xml 中添加的属性

@Override
public Map<String, Object> getProperties() 
    Map<String, Object> props = new HashMap<>();
    props.put("jersey.config.server.provider.classnames",
              "org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature");
    return props;

但即使源代码独立于实现,应用程序仍然依赖于 Jersey 角色功能。如果您决定要移植,您仍然需要替换该功能。

如果您想保持完全独立,可以自己实现该功能。这并不是那么复杂。您可以查看the source code for RolesAllowedDynamicFeature。如果您决定尝试实现相同的功能,只需使用 @Provider 注释您的实现类,它就会被选中。

【讨论】:

感谢您的出色回答。经过之前的研究,我实际上更倾向于自己实现该功能,并检查了RolesAllowedDynamicFeature 的源代码,但不知道这是否是个好主意。至少我想知道社区是否知道更好的方法。另一个问题,您是否知道在 web.xml 中配置特定于实现的 servlet(即使对于 3.0 容器)有任何问题,并且可能在那里设置包扫描属性?这样,如果我需要切换到另一个 impl,我可以更改 web.xml 配置? Jersey 提供 web.xml 配置,您可以在其中配置包扫描。但是 AFAIK, Jersey 是唯一具有包扫描功能的实现。因此,理论上,您可以利用仅针对 Jersey 的包扫描,以及使用其他 impl,只需对配置进行一些细微更改即可恢复标准行为。我没有厌倦这个,这就是我说的理论上。我很确定这不是太难,这是一个棘手的部分,要考虑什么是对端口造成最小痛苦的最佳配置组合。 再次感谢您的洞察力。我觉得做决定要好一些。 再次阅读您的问题后,请注意 RESTeasy 也有一个 roles based feature 也使用注释。因此,您可以使用简单的字符串配置(无实现依赖项)在您的应用程序中配置两者。见RESTeasy switches。我不知道将类路径扫描与提供者的 web.xml 配置相结合的行为... ...但你可以测试一下。例如,使用getProperties()(对于泽西岛,并且您可以保留对于 RESTeasy 的 if 也 - 无效)并在 web.xml 的上下文参数中注册 RESTeasy 角色功能。这对泽西岛应该没有影响。现在这都是理论上的。我也没有尝试过,但这是您可以测试的另一种选择。

以上是关于使用 javax.ws.rs.core.Application 实现通用 JAX-RS Web 服务的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)