Spring CGLIB AopProxy干扰Jersey资源方法参数验证

时间:2015-08-10 19:38:49

标签: java spring jersey aop

Stack是Spring Boot w / Jetty / Jersey。这是有问题的资源方法:

@GET
@Path("campaignTargets")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(readOnly=true)
public List<CampaignTargetOutputDTO> getCampaignTargets(
        @PathParam("businessUnitId") Integer id,
        @QueryParam("name") String name,
        @Pattern(regexp = DATE_VALIDATION_PATTERN) @QueryParam("startDate") String startDate,
        @Pattern(regexp = DATE_VALIDATION_PATTERN) @QueryParam("endDate") String endDate,
        @Pattern(regexp = INTEGER_CSV_VALIDATION_PATTERN) @QueryParam("targetTypeIds") String targetTypeIds,
        @Pattern(regexp = ALPHANUM_CSV_VALIDATION_PATTERN) @QueryParam("statuses") String statuses) {
    return ResourceUtil.entityOr404(campaignService.getAdvertiserCampaignTargets(id, name, startDate, endDate, targetTypeIds, statuses));
}

当Jersey拦截对此方法的调用以执行验证时,它不会(始终)获得此方法。我之所以知道这一点,是因为我接受了Jersey documentation的建议并创建了以下ValidationConfig

@Provider
public class ValidationConfigurationContextResolver implements
        ContextResolver<ValidationConfig> {

    @Context
    private ResourceContext resourceContext;

    @Override
    public ValidationConfig getContext(Class<?> type) {
        final ValidationConfig config = new ValidationConfig();
        config.constraintValidatorFactory(
                resourceContext.getResource(InjectingConstraintValidatorFactory.class));
        config.parameterNameProvider(new CustomParameterNameProvider());
        return config;
    }

    private static class CustomParameterNameProvider extends DefaultParameterNameProvider {

        private static final Pattern PROXY_CLASS_PATTERN = Pattern.compile("(.*?)\\$\\$EnhancerBySpringCGLIB\\$\\$.*$");
        public CustomParameterNameProvider() {
        }

        @Override
        public List<String> getParameterNames(Method method) {

            /*
             * Since we don't have a full object here, there's no good way to tell if the method we are receiving 
             * is from a proxy or the resource object itself.  Proxy objects have a class containing the string
             * $$EnhancerBySpringCGLIB$$ followed by some random digits.  These proxies don't have the same annotations
             * on their method params as their targets, so they can actually interfere with this parameter naming.
             */
            String className = method.getDeclaringClass().getName();
            Matcher m = PROXY_CLASS_PATTERN.matcher(className);
            if(m.matches()) {
                try {
                    return getParameterNames(method.getDeclaringClass().getSuperclass().
                            getMethod(method.getName(), method.getParameterTypes()));
                } catch (Exception e) {
                    return super.getParameterNames(method);
                }
            }
            Annotation[][] annotationsByParam = method.getParameterAnnotations();
            List<String> paramNames = new ArrayList<>(annotationsByParam.length);
            for(Annotation[] annotations : annotationsByParam) {
                String name = getParamName(annotations);
                if(name == null) {
                    name = "arg" + (paramNames.size() + 1);
                }
                paramNames.add(name);
            }
            return paramNames;
        }

        private String getParamName(Annotation[] annotations) {
            for(Annotation annotation : annotations) {
                if(annotation.annotationType() == QueryParam.class) {
                    return ((QueryParam) annotation).value();
                } else if(annotation.annotationType() == PathParam.class) {
                    return ((PathParam) annotation).value();
                }
            }
            return null;
        }

    }

}

我对此解决方案的主要问题是它需要一段评论(希望)以防止将来出现混淆。否则它似乎工作。如果没有这个,我会得到像arg1这样的无信息参数名称,等等,我想避免使用。这个解决方案的另一个大问题是它过分依赖Spring中Aop代理的实现。该模式可能会在未来的某个时刻更改并破坏此代码,当评论未能阐明其目的时,我可能无法在此解释此代码。最奇怪的是,这似乎是间歇性的。有时参数名称是好的,有时它们不是。任何建议都表示赞赏。

1 个答案:

答案 0 :(得分:0)

事实证明,这是因为从eclipse运行服务器而发生的。我还没弄清楚原因,但是从命令行运行服务器可以解决问题。如果有人能弄明白为什么eclipse这样做以及如何关闭任何&#34;功能&#34;日食引起了这种情况,我会upvote /接受你的回答。现在答案是,不要在eclipse中运行服务。