【问题标题】:Required @QueryParam in JAX-RS using @Required Annotation does not work in ContainerRequestFilter使用 @Required 注解的 JAX-RS 中必需的 @QueryParam 在 ContainerRequestFilter 中不起作用
【发布时间】:2016-12-03 01:43:02
【问题描述】:

我有同样的问题并尝试了 Zero3 的解决方案 (Required @QueryParam in JAX-RS (and what to do in their absence)),但在我的情况下,parameter.isAnnotationPresent(Required.class) 总是返回false

这是我的必需注释:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Required {
    // This is just a marker annotation, so nothing in here.
}

我还尝试使用 BeanParam 注释并相应地修改了过滤器,但结果相同 - 始终为 isAnnotionPresen 获取 null

我正在使用 WildFly 9 (RESTeasy),它会自动注册请求过滤器。

我的 REST 资源如下所示:

@GET
@Path("/{type}/{id}")
public Response getAllByTypeAndId(@Required @BeanParam RequiredQueryParams requiredQueryParams,
                                  @Required @QueryParam("mandant") String mandant,
                                  @PathParam("type") String type,
                                  @PathParam("id") Long id) {
...doSomething...
}

运行调试器会为 parameter.declaredAnnotations 显示 HashMap 中BeanParam 的两个条目:

0 interface my.annotations.Required -> @my.annotations.Required()
1 interface javax.ws.rs.BeanParam -> @javax.ws.rs.BeanParam()

对于QueryParam

0 interface my.annotations.Required -> @my.annotations.Required()
1 interface javax.ws.rs.QueryParam -> @javax.ws.rs.QueryParam(value=mandant)

欢迎任何提示 - 谢谢!

【问题讨论】:

  • 似乎 bean 验证更适合这个用例。查看 RESTEasy(Wildfly 的 jax-rs 实现)文档。有一节是关于 bean 验证的
  • 那将是最后一个选项,因为我正在开发一个多租户应用程序,其中我们有很多 REST 服务,并且每个服务都需要强制的“mandant”参数。我更喜欢 Zero3 的解决方案 (stackoverflow.com/questions/13968261/…) 中描述的过滤器解决方案

标签: java rest jax-rs wildfly


【解决方案1】:

根据您问题中的信息和 cmets 中的其他信息,我想我对这里发生的事情有一个大概的了解。让我们从最初的问题开始:

parameter.isAnnotationPresent(Required.class) 总是返回false

现在我们来看看AnnotatedElement#isAnnotationpresent(Class<? extends Annotation>)在Java 8中的实现:

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
    return getAnnotation(annotationClass) != null;
}

这导致我们实现Parameter#getAnnotation(Class&lt;T&gt;)

public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
    Objects.requireNonNull(annotationClass);
    return annotationClass.cast(declaredAnnotations().get(annotationClass));
}

Object#cast(Object) 的文档指出:

@return 转换后的对象,如果 obj 为 null,则返回 null

所以看起来declaredAnnotations().get(annotationClass) 返回 null。 Parameter#declaredAnnotations() 返回一个Map&lt;Class&lt;? extends Annotation&gt;, Annotation&gt;,它实际上是一个HashMap,包含注释类到所讨论参数的注释实例的映射。 Map#get(Object) 的文档指出:

@return 指定键映射到的值,如果此映射不包含该键的映射,则返回 null

这与您的评论一致,即您的 Required.class 对象不等于(也不具有相同的哈希码)相关参数上存在的 Required 注释的类对象。

这就引出了问题的核心:为什么你的注解类不等于它自己?毕竟Object#equals(object)是这样实现的:

public boolean equals(Object obj) {
    return (this == obj);
}

为了使equals(Object) 的这个实现适用于Class&lt;T&gt; 对象(它继承了实现),显然每个类只有一个这样的类对象。据我所知,比较同一个类加载器加载的类对象时也是如此。这可能在 Java 规范中的某处或某个类加载文档中的某处指定。我不知道,但我们假设事情就是这样运作的。

但是,在更高级的应用程序中,类可能由应用程序各个层的不同类加载器加载,从而使单例假设无效,进而导致您遇到的问题。所以我的猜测是,WildFly 应用程序服务器加载了您的 Required 注释,使用的类加载器与您稍后用于获取对 Required.class 对象的引用不同的类加载器。

我对 WildFly 了解不多,但我知道 Java 应用程序服务器通常依赖于一些非常官僚的类加载器层次结构来加载类(出于安全性和其他原因)。我建议查看 WildFly 文档,希望有更多关于此的信息。

TL;DR:我认为您的 Required.class 对象代表了您的 Required 注释类的不同副本,而不是参数上存在的副本。确保使用相同的类加载器来加载两者。

【讨论】:

  • 感谢您非常详细的解释。关于您保证应该使用相同的类加载器的提示,我重构了我的应用程序,isAnnotationPresent() 按预期工作!我会将您的评论标记为对这个问题的回答!
【解决方案2】:

作为ContainerRequestFilter 中不工作的parameter.isAnnotationPresent(Required.class) 的解决方法,我现在正在使用这种方法:

private boolean isRequired(Parameter parameter) {
    for (Annotation annotation : parameter.getDeclaredAnnotations()) {
        if (Required.class.getName().equals(annotation.annotationType().getName())) {
            return true;
        }
    }
    return false;
}

不管怎样,我想知道为什么 isAnnotionaPresent() 不起作用?!

【讨论】:

  • 有趣。您是否在注释类型中做一些有趣的事情?喜欢覆盖equals()hashCode()?由于您通过注释类名称进行比较(当然,这不是一种非常可靠的方法),我假设您在直接与 equals() 比较时遇到问题?调试提示:尝试在您现在比较的名称旁边打印equals()hashcode() 的结果,看看是否正确。
  • 嗨 Zero3,我的注释看起来和你的一样:@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Required { // This is just a marker annotation, so nothing in here. } 比较名称中的hashCode() 是相等的,所以我的解决方法应该是正确的;-)
  • 嗯。实际上,我的意思是您可以尝试打印Required.class.hashcode()annotation.annotationType().hashcode()Required.class.equals(annotation.annotationType())。如果这些与预期的不同/真实,那可能是您遇到麻烦的原因。另一个原因当然可能是您在端点 Java 文件中错误地导入了错误的 Required 注释(您没有在原始帖子中发布完整的代码示例,因此很难在此处获得完整的图片)。
  • 我将Required的实现添加到原始帖子中。
  • 这里是我的调试输出:Required.class=interface at.luxbau.mis2.commons.annotation.Requiredannotation.annotationType()=interface at.luxbau.mis2.commons.annotation.RequiredRequired.class.equals(annotation.annotationType())=false // false, because hashCode() returns different values?!Required.class.hashCode()=145058991annotation.annotationType().hashCode()=446212579Required.class.getName()=at.luxbau.mis2.commons.annotation.Requiredannotation.annotationType().getName()=at.luxbau.mis2.commons.annotation.Required
猜你喜欢
  • 1970-01-01
  • 2013-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-14
  • 1970-01-01
  • 2013-04-19
相关资源
最近更新 更多