有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java从Jersey筛选器或拦截器内部访问资源方法参数。或者将AOP与资源方法一起使用

我试图在每个请求上使用用户ID丰富SLF4J MDC。问题是ID可以通过多种方式传递,有时作为路径参数传递,有时在正文中传递,有时由首先解密它的自定义ValueFactoryProvider注入

如果我能以某种方式访问所有注入的(即,已反序列化的)参数值,我就能轻松处理所有这些情况

例如

对于资源,例如:

@GET
//@Encrypted params are injected by a custom ValueFactoryProvider
public Something getSomething(@Encrypted("userId") String userId) {
    return ...;
}

@POST
public Something getSomething(@RequestBody RequestWithUserId requestWithUserId) {
    return ...;
}

我可以有一个过滤器,例如:

public class MdcFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        Method theMethod = resourceInfo.getResourceMethod();
        for (Parameter parameter : theMethod.getParameters()) {
            //Deal with the @Encrypted case
            if (parameter.isAnnotationPresent(Encrypted.class) && parameter.getAnnotation(Encrypted.class).value().equals("userId")) {
                MDC.put("userId", somehowGetTheValue());
            }
            //Deal with the @RequestBody case
            if (parameter.isAnnotationPresent(RequestBody.class) && parameter.getType().equals(RequestWithUserId.class)) {
                MDC.put("userId", ((RequestWithUserId)somehowGetTheValue()).getUserId());
            }
            ... //other possibilities
        }
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        MDC.clear();
    }
}

但是我看不到一种实现somehowGetTheValue的方法,无论是从ContainerRequestFilter拦截器还是其他任何东西


共 (2) 个答案

  1. # 1 楼答案

    你可以这样得到它

    StringWriter stringWriter = new StringWriter();
    IOUtils.copy(new InputStreamReader(requestContext.getEntityStream()), stringWriter);
    System.out.println(stringWriter.toString());// String representation of the payload
    requestContext.setEntityInputStream(new ByteArrayInputStream(requestEntity));
    

    基本上,这个想法是复制流并进行任何处理,然后将流设置回原来的位置。因为如果您不这样做,那么在您的控制器方法中,您将得到null,因为流已经被读取

  2. # 2 楼答案

    Jersey在引擎盖下使用HK2进行依赖注入。而香港2号则有AOP support。用例的一个选项是使用此AOP支持。您所需要做的就是实现一个^{}和一个^{}。在MethodInterceptor中,可以从MethodInvocation获取所有参数,也可以从Method获取参数注释

    class MyMethodInteceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            Method method = invocation.getMethod();
            Object[] args = invocation.getArguments();
    
            // do your logging or whatever with the args.
    
            // invoke method and get return value.
            Object returnValue = invocation.proceed();
            // if you want to do something with the return
            // value before returning it, you can.
    
            return returnValue;
        }
    }
    

    要使用拦截器,请配置InterceptionService

    public class MyInterceptionService implements InterceptionService {
    
        private final static MethodInterceptor METHOD_INTERCEPTOR 
                = new MyMethodInterceptor();
        private final static List<MethodInterceptor> METHOD_LIST
                = Collections.singletonList(METHOD_INTERCEPTOR);
    
        @Override
        public Filter getDescriptorFilter() {
            return BuilderHelper.allFilter();
        }
    
        @Override
        public List<MethodInterceptor> getMethodInterceptors(Method method) {
            // you implement shouldIntercept
            if (shouldIntercept(method)) {
                return METHOD_LIST;
            }
            return null;
        }
    
        @Override
        public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) {
            return null;
        }
    }
    

    您可以确定应该在getMethodInterceptors()方法中拦截哪个方法。如果应该拦截该方法,则返回拦截器列表,否则返回null。处理这个问题的一种常见方法是创建一个自定义注释,然后只对方法进行注释。在上述方法中,只需检查

    if (method.isAnnotationPresent(YourAnno.class)) {
        return METHOD_LIST;
    }
    

    要使这一切正常工作,您只需向HK2注册InteceptionService。您可以在AbstractBinder中执行此操作,这是Jersey应用程序中用于配置DI的内容

    ResourceConfig config = new ResourceConfig();
    config.register(new AbstractBinder() {
        @Override
        protected void configure() {
            bind(MyInterceptionService.class)
                    .to(InterceptionService.class)
                    .in(Singleton.class);
        }
    });
    

    您可以在this GitHub repo中看到一个完整的示例。在HK2网站上也有一个官方的例子。请参见文章顶部的“AOP支持”链接