Java spring注释可以访问方法参数吗?
考虑一个Java spring注释可以访问方法参数吗?,java,spring,annotations,Java,Spring,Annotations,考虑一个urlvalidater方法注释,该注释在调用方法之前测试给定的url是否有效 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UrlValdator{ String value(); } 当路由是静态的并且提前知道时,这种方法可以很好地工作。例如: @UrlValidator("http://some.known.url") public void doSome
urlvalidater
方法注释,该注释在调用方法之前测试给定的url是否有效
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlValdator{
String value();
}
当路由是静态的并且提前知道时,这种方法可以很好地工作。例如:
@UrlValidator("http://some.known.url")
public void doSomething();
但这不是很灵活。例如,如果路由隐含在doSomething()
方法签名中,该怎么办?我可以通过Spring表达式语言或其他方式访问它吗?例如,这不起作用,但这正是我拍摄的目的
@UrlValidator("#p1")
public void doSomething(String url)
或
这样可以使注释动态化吗
相关的
,但思路陈旧,公认的答案非常繁琐/难以理解。是否有一个最简单的工作示例/更新的方法可以做到这一点?简短回答:是
长答覆:
指定批注的目标,可以是以下对象:批注类型、构造函数、字段、局部变量、方法、包、模块、参数、类型和类型参数
。我们对这里的参数
感兴趣。由于我们希望从编译器运行代码,RetentionPolicy.RUNTIME
适合保留类型。
接下来,我们必须添加@Constraint
注释,根据文档:
将注释标记为Bean验证约束
这意味着,Spring将获取您的参数并在运行时验证它。我们必须做的最后一件事是实现验证本身,这意味着创建一个实现接口的类
总而言之:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UrlValidatorImplementation.class)
public @interface UrlValidator{
String message() default "Invalid url";
}
urlvalidateImplementation
类的实现:
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
}
}
我不完全确定这是否是您的想法,但我可以建议使用SpringAOP,因为它可以给您很大的灵活性 由于您在其中一条评论中提到您已经在使用Spring AOP,我将假设您已经添加了
Spring boot starter AOP
作为依赖项,并且您已经通过使用@enableSpectAOP
例如,定义了这样的注释:
@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;
}
}
@方面
@组成部分
公共类UrlValidator{
@周围(value=“@annotation(EnsureUrlValid)”)
公共对象检查URL(ProceedingJoinPoint joinPoint)抛出可丢弃的{
最终可选urlOpt=提取URL(连接点);
if(urlOpt.isPresent()){
最后一个字符串url=urlOpt.get();
if(isUrlValid(url)){
返回joinPoint.procedure();
}
}
抛出新的RuntimeException(“传入的url无法解析或无效”);
}
私有可选提取URL(连接点连接点){
Object[]methodArgs=joinPoint.getArgs();
对象rawUrl=null;
if(methodArgs.length==1){
rawUrl=methodArgs[0];
}
否则如果(methodArgs.length>1){
//检查已标记要验证的参数
方法Method=((MethodSignature)joinPoint.getSignature()).getMethod();
Parameter[]parameters=method.getParameters();
布尔值=false;
int i=0;
而(i
我希望它能有所帮助。您可以尝试spring AOP,围绕@UrlValidator注释的控制器方法创建切入点。我已经在使用spring AOP,但不明白切入点如何帮助我实现这一点。你能举个例子吗?谢谢。让我尽快试用,然后回来标记接受/投票。是的,我正在使用APO。这看起来确实像我的想法。并不是说这是非常必要的,而是当场位于一个对象上时会发生什么情况呢。例如
@urlvalidater(obj.url)
。这可能不可行,对吧?我询问的唯一原因是,我想要验证的一些URL是在传递到方法中的对象上。也许重构是按顺序进行的:0ConstraintValidator
——而不是您传入任何所需的对象的String
。此外,验证方法将具有以下签名:public boolean isValid(对象对象、约束验证或上下文上下文)
@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");
}
}
@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;
}
}