簡體   English   中英

Spring注釋可以訪問方法參數嗎?

[英]Can spring annotation access method parameters?

考慮一個UrlValidator方法注釋,該注釋在調用方法之前測試給定的url是否有效。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlValdator{
    String value();
}

當路由是靜態的並且提前知道時,這可以正常工作。 例如:

@UrlValidator("http://some.known.url")
public void doSomething();

但這不是很靈活。 例如,如果路由在doSomething()方法簽名中是隱式的,該怎么辦? 我可以以某種方式通過Spring Expression Language或其他方式訪問它嗎? 例如, 這不起作用,但這是我要拍攝的

@UrlValidator("#p1")
public void doSomething(String url)

要么

@UrlValidator("#p1.url")
public void doSomething(Request request)

是否可以通過這種方式使注釋動態化?

有關

這是我找到的最接近的線程,但是線程較舊,可以接受的答案非常麻煩/難以遵循。 是否有一個最小的工作示例/更新的方式來做到這一點?

我不確定這是否是您的初衷,但是我建議使用Spring AOP,因為它可以為您提供很大的靈活性。

既然您在評論之一中提到您已經在使用Spring AOP,所以我假設您已將spring-boot-starter-aop為依賴項,並且已啟用對組件的支持。通過使用@EnableAspectJAutoProxy注釋您的一個配置類來標記@Aspect

例如,具有這樣的注釋:

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface UrlToVerify {
}

我可以在示例彈簧組件中使用它們,如下所示:

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

這是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;
    }
}

我希望這會有所幫助。

簡短的回答:是的。

長答案: ElementType指定注釋的目標,可以是以下內容: ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE, and TYPE_PARAMETER 在這里對PARAMETER感興趣。 由於我們希望編譯器運行我們的代碼,因此RetentionPolicy.RUNTIME適合保留類型。 接下來,我們必須添加@Constraint批注,根據文檔:

將注釋標記為Bean驗證約束。

這意味着,Spring將選擇您的參數並在運行時對其進行驗證。 我們要做的最后一件事是實現驗證本身,這意味着創建一個實現ConstraintValidator接口的類。

放在一起:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UrlValidatorImplementation.class)
public @interface UrlValidator{
    String message() default "Invalid url";
}

UrlValidatorImplementation類的實現:

public class UrlValidatorImplementation implements ConstraintValidator<UrlValidator, String> {
    @Override
    public void initialize(UrlValidator annotation) {
        // initialization, probably not needed
    }

    @Override
    public boolean isValid(String url, ConstraintValidatorContext context) {
        // implementation of the url validation
    }
}

注釋的用法:

public void doSomething(@UrlValidator url) { ... }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM