考虑一个doSomething()
方法注释,在调用方法之前测试给定的url是否有效。
@UrlValidator("#p1")
public void doSomething(String url)
当路由是静态的并且提前知道时,这工作正常。例如:
@UrlValidator("#p1.url")
public void doSomething(Request request)
但这不是很灵活。例如,如果路由隐含在github "mxcl/OMGHTTPURLRQ" "3.1.1"
github "mxcl/PromiseKit" "3.5.1"
github "pusher/pusher-websocket-swift" "swift-2.3"
github "getsentry/sentry-cocoa" "3.2.0"
方法签名中,该怎么办?我可以以某种方式从Spring Expression Language或其他方式访问它吗?例如,这不起作用,但我正在拍摄
data.frame
或
top_n
是否可以通过这种方式创建注释?
SRFI-1 List Library,但线程已经过时,接受的答案是繁琐/难以理解的。是否有最小的工作示例/更新方式来执行此操作?
答案 0 :(得分:3)
我不完全确定这是不是你的想法,但我可以建议使用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 {
}
我可以在样本弹簧组件中使用它们,如下所示:
@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;
}
}
我希望它有所帮助。
答案 1 :(得分:1)
简短回答:是的。
答案很长:
ElementType指定注释的目标,可以是以下内容:ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE, and TYPE_PARAMETER
。我们对PARAMETER
感兴趣。由于我们希望从编译器运行我们的代码,RetentionPolicy.RUNTIME
适用于保留类型。
接下来,我们必须添加@Constraint
注释,根据文档:
将注释标记为Bean Validation约束。
这意味着,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) { ... }