如何在Jersey调用之前获取与URI匹配的资源方法?

我正在尝试实现一个ContainerRequestFilter ,它对请求的参数进行自定义validation。 我需要查找将与URI匹配的资源方法,以便我可以从方法的参数中删除自定义注释。

根据这个答案,我应该能够注入ExtendedUriInfo ,然后使用它来匹配方法:

 public final class MyRequestFilter implements ContainerRequestFilter { @Context private ExtendedUriInfo uriInfo; @Override public ContainerRequest filter(ContainerRequest containerRequest) { System.out.println(uriInfo.getMatchedMethod()); return containerRequest; } } 

getMatchedMethod显然会返回null ,一直到实际调用该方法为止(此时对我来说进行validation为时已晚)。

在调用资源方法之前,如何检索将与给定URI匹配的方法?


对于那些感兴趣的人,我正在尝试滚动我自己的必需参数validation,如JERSEY-351中所述 。

实际上,您应该尝试将ResourceInfo注入自定义请求filter。 我用RESTEasy尝试过,它在那里工作。 优点是您针对JSR接口而不是Jersey实现进行编码。

 public class MyFilter implements ContainerRequestFilter { @Context private ResourceInfo resourceInfo; @Override public void filter(ContainerRequestContext requestContext) throws IOException { Method theMethod = resourceInfo.getResourceMethod(); return; } } 

我想出了如何使用Jersey解决我的问题。 显然没有办法将请求的URI与调用该方法之前匹配的方法相匹配,至少在Jersey 1.x中是这样。 但是,我能够使用ResourceFilter为每个单独的资源方法创建一个ResourceFilter – 这样这些filter可以提前了解目标方法。

这是我的解决方案,包括对所需查询参数的validation(使用Guava和JSR 305):

 public final class ValidationFilterFactory implements ResourceFilterFactory { @Override public List create(AbstractMethod abstractMethod) { //keep track of required query param names final ImmutableSet.Builder requiredQueryParamsBuilder = ImmutableSet.builder(); //get the list of params from the resource method final ImmutableList params = Invokable.from(abstractMethod.getMethod()).getParameters(); for (Parameter param : params) { //if the param isn't marked as @Nullable, if (!param.isAnnotationPresent(Nullable.class)) { //try getting the @QueryParam value @Nullable final QueryParam queryParam = param.getAnnotation(QueryParam.class); //if it's present, add its value to the set if (queryParam != null) { requiredQueryParamsBuilder.add(queryParam.value()); } } } //return the new validation filter for this resource method return Collections.singletonList( new ValidationFilter(requiredQueryParamsBuilder.build()) ); } private static final class ValidationFilter implements ResourceFilter { final ImmutableSet requiredQueryParams; private ValidationFilter(ImmutableSet requiredQueryParams) { this.requiredQueryParams = requiredQueryParams; } @Override public ContainerRequestFilter getRequestFilter() { return new ContainerRequestFilter() { @Override public ContainerRequest filter(ContainerRequest request) { final Collection missingRequiredParams = Sets.difference( requiredQueryParams, request.getQueryParameters().keySet() ); if (!missingRequiredParams.isEmpty()) { final String message = "Required query params missing: " + Joiner.on(", ").join(missingRequiredParams); final Response response = Response .status(Status.BAD_REQUEST) .entity(message) .build(); throw new WebApplicationException(response); } return request; } }; } @Override public ContainerResponseFilter getResponseFilter() { return null; } } } 

并且ResourceFilterFactory在Jersey中注册为web.xml servlet的init参数:

  com.sun.jersey.spi.container.ResourceFilters my.package.name.ValidationFilterFactory  

在启动时,将为Jersey检测到的每个资源方法调用ValidationFilterFactory.create

感谢这篇文章让我走上正轨: 如何在Jersey ContainerResponseFilter中获取资源注释

我知道你正在寻找一个仅限泽西岛的解决方案,但这里有一个Guice方法可以让事情发挥作用:

 public class Config extends GuiceServletContextListener { @Override protected Injector getInjector() { return Guice.createInjector( new JerseyServletModule() { @Override protected void configureServlets() { bindInterceptor(Matchers.inSubpackage("org.example"), Matchers.any(), new ValidationInterceptor()); bind(Service.class); Map params = Maps.newHashMap(); params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example"); serve("/*").with(GuiceContainer.class, params); } }); } public static class ValidationInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation method) throws Throwable { System.out.println("Validating: " + method.getMethod()); return method.proceed(); } } } 
 @Path("/") public class Service { @GET @Path("service") @Produces({MediaType.TEXT_PLAIN}) public String service(@QueryParam("name") String name) { return "Service " + name; } } 

编辑:性能比较:

 public class AopPerformanceTest { @Test public void testAopPerformance() { Service service = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bindInterceptor(Matchers.inSubpackage("org.example"), Matchers.any(), new ValidationInterceptor()); } }).getInstance(Service.class); System.out.println("Total time with AOP: " + timeService(service) + "ns"); } @Test public void testNonAopPerformance() { System.out.println("Total time without AOP: " + timeService(new Service()) + "ns"); } public long timeService(Service service) { long sum = 0L; long iterations = 1000000L; for (int i = 0; i < iterations; i++) { long start = System.nanoTime(); service.service(null); sum += (System.nanoTime() - start); } return sum / iterations; } } 

在resteasy-jaxrs-3.0.5中,您可以从ContainerRequestContext.getProperty()检索表示匹配资源方法的ResourceMethodInvoker

  import org.jboss.resteasy.core.ResourceMethodInvoker; public class MyRequestFilter implements ContainerRequestFilter { public void filter(ContainerRequestContext request) throws IOException { String propName = "org.jboss.resteasy.core.ResourceMethodInvoker"; ResourceMethodInvoker invoker = (ResourceMethodInvoker)request.getProperty(); invoker.getMethod().getParameterTypes().... } }