Spring注释可以访问方法参数吗?

Ada*_*hes 1 java spring annotations

考虑一个UrlValidator方法注释,该注释在调用方法之前测试给定的URL是否有效。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlValdator{
    String value();
}
Run Code Online (Sandbox Code Playgroud)

当路由是静态的并且提前知道时,这可以正常工作。例如:

@UrlValidator("http://some.known.url")
public void doSomething();
Run Code Online (Sandbox Code Playgroud)

但这不是很灵活。例如,如果路由在doSomething()方法签名中是隐式的,该怎么办?我可以以某种方式通过Spring Expression Language或其他方式访问它吗?例如,这不起作用,但这是我要拍摄的

@UrlValidator("#p1")
public void doSomething(String url)
Run Code Online (Sandbox Code Playgroud)

要么

@UrlValidator("#p1.url")
public void doSomething(Request request)
Run Code Online (Sandbox Code Playgroud)

是否可以通过这种方式使注释动态化?

有关

这是我找到的最接近的东西,但是线程很旧,可以接受的答案非常麻烦/难以遵循。是否有一个最小的工作示例/更新的方式来做到这一点?

小智 5

我不确定这是否是您的初衷,但是我建议使用Spring AOP,因为它可以为您提供很大的灵活性。

既然您在其中一条注释中提到您已经在使用Spring AOP,那么我将假定您已添加spring-boot-starter-aop为依赖项,并且已通过注释一个注释来启用了对使用@Aspect标记的组件的支持。您的配置类@EnableAspectJAutoProxy

例如,具有这样的注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EnsureUrlValid {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface UrlToVerify {
}
Run Code Online (Sandbox Code Playgroud)

我可以在示例弹簧组件中使用它们,如下所示:

@Component
public class SampleComponent {

    private static final Logger logger = LogManager.getLogger(SampleComponent.class);

    @EnsureUrlValid
    public void fetchData(String url) {
        logger.info("Fetching data from " + url);
    }

    @EnsureUrlValid
    public long fetchData(Long id, @UrlToVerify String url) {
        logger.info("Fetching data for user#" + id + " from " + url);
        // just to show that a method annotated like this can return values too
        return 10L;
    }

    @EnsureUrlValid
    public void fetchDataFailedAttempt() {
        logger.info("This should not be logged");
    }
}
Run Code Online (Sandbox Code Playgroud)

这是EnsureUrlValid注释的示例“处理器” 。它查找带注释的方法,尝试提取传入的url,并根据url是否有效,继续调用该方法或引发异常。很简单,但是它表明您可以完全控制所注释的方法。

@Aspect
@Component
public class UrlValidator {

    @Around(value = "@annotation(EnsureUrlValid)")
    public Object checkUrl(ProceedingJoinPoint joinPoint) throws Throwable {
        final Optional<String> urlOpt = extractUrl(joinPoint);
        if (urlOpt.isPresent()) {
            final String url = urlOpt.get();
            if (isUrlValid(url)) {
                return joinPoint.proceed();
            }
        }
        throw new RuntimeException("The passed-in url either could not be resolved or is not valid");
    }

    private Optional<String> extractUrl(JoinPoint joinPoint) {
        Object[] methodArgs = joinPoint.getArgs();

        Object rawUrl = null;
        if (methodArgs.length == 1) {
            rawUrl = methodArgs[0];
        }
        else if (methodArgs.length > 1) {
            // check which parameter has been marked for validation
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            Parameter[] parameters = method.getParameters();
            boolean foundMarked = false;
            int i = 0;
            while (i < parameters.length && !foundMarked) {
                final Parameter param = parameters[i];
                if (param.getAnnotation(UrlToVerify.class) != null) {
                    rawUrl = methodArgs[i];
                    foundMarked = true;
                }
                i++;
            }
        }

        if (rawUrl instanceof String) { // if rawUrl is null, instanceof returns false
            return Optional.of((String) rawUrl);
        }
        // there could be some kind of logic for handling other types

        return Optional.empty();
    }

    private boolean isUrlValid(String url) {
        // the actual validation logic
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这会有所帮助。