【问题标题】:How do I intercept selective methods and classes in JAX-RS in Java EE 7 compliant container?如何在符合 Java EE 7 的容器中截取 JAX-RS 中的选择性方法和类?
【发布时间】:2014-10-24 17:31:24
【问题描述】:

我想拦截任何带有@Foo注释的classmethods

类级拦截:

@Foo
@path("/foo")
public class Attack {...}

方法级拦截:

@path("/bar")
public class defend {

@Foo
@GET
public String myMethod(){....}

我想拦截使用@Foo 注释的任何类或方法,但不拦截其他方法或类。我想在继续执行方法之前打印出整个路径或 URI。一个方法调用完成,我想打印出“执行成功”

就是这样的:

 system.out.println(path) // this is the path the request is made. something like /api/2/imp/foo
   method call happens
   method call finishes
   System.out.println("executed successfully")

我的情况不同,但这是我遇到的根本问题。我不想具体实现。 Java EE 7 规范有一种方法可以使用 @Postconstruct、@AroundInvoke 等来实现这一点。但我真的很难组装它。

post 绝对是解决这个问题的好方法。但它是特定于实现的 (RESTeasy),它使用的 AcceptByMethod 已被弃用。

谢谢

【问题讨论】:

标签: java jax-rs interceptor java-ee-7


【解决方案1】:

浏览Java EE Tutorial for JAX-RS,似乎他们没有提及jsr339-jaxrs-2.0-final-spec 中有关过滤器和拦截器的概念。您可能应该下载一份副本以获取完整信息。

过滤器和实体拦截器可以注册以在 JAX-RS 中定义明确的扩展点处执行 实施。它们用于扩展实现以提供诸如日志记录、 机密性、身份验证、实体压缩

实体拦截器在特定的扩展点包装方法调用。 过滤器在扩展点执行代码,但不包装方法调用。

最后一段基本上是说拦截器与方法调用发生在同一个执行堆栈中,而过滤器则没有。这并不意味着我们不能为您的日志记录案例使用过滤器。传递给过滤器接口方法的过滤器上下文实际上有更多可以使用的信息。

ContainerRequestFilterContainerResponseFilter 分别通过ContainerRequestContextContainerResponseContext,我们可以获取UriInfo 之类的东西来获取路径。

public interface ContainerResponseFilter {
    void filter(ContainerRequestContext requestContext, 
           ContainerResponseContext responseContext)
}

public interface ContainerRequestFilter {
    void filter(ContainerRequestContext requestContext)
}

这是一个简单的日志过滤器示例。有几种不同的方法来绑定过滤器,但在这个例子中,我将使用dynamic binding 显式实例化过滤器,因此我没有容器管理状态,并将类和方法名称传递给过滤器

public class LoggingFilter implements ContainerRequestFilter,
                                      ContainerResponseFilter {

    private static final Logger logger
            = Logger.getLogger(LoggingFilter.class.getName());

    protected String className;
    protected String methodName;

    public NewLoggingFilter(String className, String methodName) {
        this.className = className;
        this.methodName = methodName;
    }

    @Override
    public void filter(ContainerRequestContext requestContext) 
                                                      throws IOException {
        logger.log(Level.INFO, "Request path: {0}",
                requestContext.getUriInfo().getAbsolutePath().toString());
        logger.log(Level.INFO, "Starting Method: {0}.{1}",
                new Object[]{className, methodName});
    }

    @Override
    public void filter(ContainerRequestContext requestContext,
                       ContainerResponseContext responseContext)
                                                       throws IOException {

        logger.log(Level.INFO, "Finished Method: {0}.{1}",
                                       new Object[]{className, methodName});
    }
}

以下是我将方法绑定到过滤器的方法。每个资源方法都经过这个活页夹。如果它或它的类是带有我们自定义注解的注解,它将被绑定到LoggingFilter。我们还传递了LogginFilter 资源方法的类和方法名。我们将使用这些名称进行日志记录

@Provider
public class LoggingBinder implements DynamicFeature {

    @Override
    public void configure(ResourceInfo ri, FeatureContext fc) {
        Class<?> clazz = ri.getResourceClass();
        Method method = ri.getResourceMethod();
        if (method.isAnnotationPresent(Logged.class) 
                || clazz.isAnnotationPresent(Logged.class)) {
            fc.register(new LoggingFilter(clazz.getName(), method.getName()));
        }
    }  
}

它检查方法或类是否有注解@Logged(这是一个自定义注解——你可以很容易地调用它@Foo

@NameBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface Logged {
}

使用这个资源类

@Path("/log")
public class LogResource {
    @GET
    @Logged
    public Response getLoggingResourceMethod() {
        return Response.ok("Hello Logging Response").build();
    }
}

我们在日志中得到以下结果

Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Request path: http://localhost:8081/rest/log
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Starting Method: jaxrs.stackoverflow.filter.LogResource.getLoggingResourceMethod
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Finished Method: jaxrs.stackoverflow.filter.LogResource.getLoggingResourceMethod
Oct 25, 2014 4:36:05 PM jaxrs.stackoverflow.filter.NewLoggingFilter filter
INFO: Method successful.

不要忘记下载规范以获取更多详细信息。

【讨论】:

  • 感谢您的详细回答。我了解如何使用过滤器。但这并不能完全回答我的问题,即使用拦截器选择性地拦截带有@Foo 的方法或类并执行日志记录。也就是说,通过使用@Foo 进行注释并使用拦截器,我将其记录下来。你能在我上面的例子中提供如何使用拦截器的例子吗?非常感谢
  • JAX-RS 拦截器与 Java EE 拦截器不同。 Jax-RS 拦截器用于操纵实体主体。我不确定您是否/如何将 Java EE 拦截器用于 JAX-RS 资源。我会调查的
  • 使用过滤器示例,您可以简单地将@Logged 注释(这是我创建的注释)更改为@FooLoggingBinder 类将所有用 @Logged/@Foo 注释的方法/类绑定到过滤器。日志记录是 JAX-RS 过滤器的常见用例
  • 感谢您的来信。实际上,我真正需要的不是记录器,而是其他一些逻辑。我只是以日志记录为例。我们当前的代码使用拦截器中的AcceptByMethod 绑定到RESTeasy 来执行逻辑。我们正在转向 Java EE 7,我们不想将自己与 RESTeasy 捆绑在一起,而是更通用。我正在为这个过渡编写代码。它之前是使用拦截器实现的。所以我想知道这是否可能。如果您查看我上面提到的博客文章,它完全符合我的要求,但使用的是 RESTeasy。
【解决方案2】:

拦截器真的很简单:

@Foo @Interceptor
public class FooInterceptor
{
    @AroundInvoke
    public Object handleFoo(InvocationContext joinPoint) throws Exception
    {
        Method m = joinPoint.getMethod();

        // you can access all annotations on your @Foo-annotated method,
        // not just the @Foo annotation.
        Annotation[] as = m.getDeclaredAnnotations();

        // do stuff before the method call
        ...

        try
        {
            // here you call the actual method
            return joinPoint.proceed();
        }
        finally
        {
            // do stuff after the method call
            ...
        }
    }
}

这是注释的外观:

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Foo
{
    @Nonbinding
    ... // you could add parameters for your annotation, e.g. @Foo(value)
}

这就是你将如何使用它:

@Stateless
public class MyService
{
    @Foo("bar")
    public String myWrappedMethod()
    {
        ...
    }
}

myWrappedMethod 中的代码将被 FooInterceptor 中的代码“包装”。 请注意,仅当对 myWrappedMethod() 的方法调用由容器管理时才会调用拦截器,即您在 MyService 的托管实例上调用它(例如,通过 @Inject)

【讨论】:

  • 太棒了,我期待着这些方面的东西。如果您有这方面的工作示例,请发布
  • 这或多或少是从我的项目中的实际代码中提取的(标识符名称已更改以保护无辜者;-)所以是的,这是“工作代码”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
相关资源
最近更新 更多