简体   繁体   English

如何在符合Java EE 7的容器中拦截JAX-RS中的选择性方法和类?

[英]How do I intercept selective methods and classes in JAX-RS in Java EE 7 compliant container?

I want to intercept any class or methods annotated with @Foo 我想拦截用@Foo注释的任何classmethods

Class level interception: 级别拦截:

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

Method level interception: 方法级别拦截:

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

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

I want to intercept any class or methods annotated with @Foo but not other methods or classes. 我想拦截用@Foo注释的任何类或方法,但不拦截其他方法或类。 I want to print out the entire path or URI before proceeding to the method execution. 我想在继续执行方法之前打印出整个路径或URI。 One the method call is finished, I want to print out "executed sucessfully" 一个方法调用完成,我想打印出“执行成功”

That is something of this kind: 这是这样的事情:

 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")

My scenario is different but this is the fundamental problem I have. 我的情况不同,但这是我的根本问题。 I do not want to be implementation specific. 我不想具体实施。 Java EE 7 specification has a way to do this using @Postconstruct, @AroundInvoke etc. But I am really having difficulty assembling this. Java EE 7规范有一种方法可以使用@Postconstruct,@ AroundInvoke等来实现这一点。但是我真的很难组装它。

This post is definitely a great approach to this problem. 这篇文章绝对是解决这个问题的好方法。 but it is implementation specific (RESTeasy) and the AcceptByMethod it uses is deprecated. 但它是特定于实现的(RESTeasy),并且不推荐使用它所使用的AcceptByMethod

Thanks 谢谢

Skimming through the Java EE Tutorial for JAX-RS , it seems they fail to mention anything about the concept of Filters and Interceptors from the jsr339-jaxrs-2.0-final-spec . 浏览JAX-RSJava EE教程 ,似乎没有提到jsr339-jaxrs-2.0-final-spec中滤波器和拦截器概念的任何内容。 You should probably download a copy for complete information. 您应该下载副本以获取完整信息。

Filters and entity interceptors can be registered for execution at well-defined extension points in JAX-RS implementations. 可以在JAX-RS实现中的明确定义的扩展点处注册过滤器和实体拦截器以执行。 They are used to extend an implementation in order to provide capabilities such as logging, confidentiality, authentication, entity compression 它们用于扩展实现,以提供日志记录,机密性,身份验证,实体压缩等功能

Entity interceptors wrap around a method invocation at a specific extension point. 实体拦截器环绕特定扩展点的方法调用。 Filters execute code at an extension point but without wrapping a method invocation. 过滤器在扩展点执行代码,但不包装方法调用。

Basically the last paragraph is saying that interceptors occur in the same execution stack as the method call, whereas filters don't. 基本上最后一段是说拦截器出现在与方法调用相同的执行堆栈中,而过滤器则没有。 That doesn't mean we can't use filters for your logging case. 这并不意味着我们不能为您的日志记录案例使用过滤器。 The filter contexts passed to the filter interface method actually have a lot more information that you can use. 传递给过滤器接口方法的过滤器上下文实际上有更多可以使用的信息。

The ContainerRequestFilter and ContainerResponseFilter get passed ContainerRequestContext and ContainerResponseContext , respectively, of which we can obtain things like the UriInfo to get the path from. ContainerRequestFilterContainerResponseFilter分别传递ContainerRequestContextContainerResponseContext ,我们可以从中获取UriInfo类的UriInfo来获取路径。

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

public interface ContainerRequestFilter {
    void filter(ContainerRequestContext requestContext)
}

Here's a simple example of a logging filter. 这是一个日志过滤器的简单示例。 There a few different ways to bind the filter, but with this example I'll use dynamic binding where I instantiate the filter explicitly, Therefore I don't have a container managed state, and pass the class and method names to the filter 有几种不同的方法来绑定过滤器,但是在这个例子中,我将使用动态绑定 ,其中我明确地实例化过滤器,因此我没有容器管理状态,并将类和方法名称传递给过滤器

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});
    }
}

Here's how I bind the methods to the filter. 这是我如何将方法绑定到过滤器。 Every resource method goes through this binder. 每种资源方法都通过这个绑定器。 If it or it's class is annotation with our custom annotation, it will be binded to out LoggingFilter . 如果它或它的类是带有我们的自定义注释的注释,它将被绑定到LoggingFilter We also pass the LogginFilter the class and method names of the resource method. 我们还传递了LogginFilter资源方法的类和方法名称。 We will use those names for our logging 我们将使用这些名称进行日志记录

@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()));
        }
    }  
}

It checks the method or class to see if it has the annotation @Logged (which is a custom annotation - you can just as easily call it @Foo ) 它检查方法或类以查看它是否具有注释@Logged (这是一个自定义注释 - 您可以轻松地将其@Foo

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

Using this resource class 使用此资源类

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

We get the following result in our log 我们在日志中得到以下结果

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.

Don't forget to download the spec for more details. 不要忘记下载规范以获取更多详细信息。

An Interceptor is really simple: 拦截器非常简单:

@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
            ...
        }
    }
}

This is how the annotation would look: 这是注释的外观:

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

And this is how you would use it: 这就是你如何使用它:

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

The code inside myWrappedMethod would be "wrapped" by the code in the FooInterceptor. myWrappedMethod中的代码将由FooInterceptor中的代码“包装”。 Note that the interceptor is only invoked if the method call to myWrappedMethod() is managed by the container, ie you invoke it on a managed instance of MyService (eg via @Inject) 请注意,只有在对容器管理myWrappedMethod()的方法调用时,才会调用拦截器,即在MyService的托管实例上调用它(例如,通过@Inject)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM