如何正确使用 Spring AOP 来 select 执行带有特定注解的方法?
How to correctly use Spring AOP to select the execution of a method annotated with a specific annotation?
我正在学习 Spring AOP 并且我有以下关于我学习中发现的问题的疑问 material.
因此请考虑以下切入点:execution(@com.myapp.MyCustomAnnotation void *(..))。具体是什么意思?
它给了我以下答案,在我看来有点奇怪(使用我对 AOP 工作原理的了解)。它说:
This will select joint points representing voiud method that are
annotated by @com.myapp.MyCustomAnnotation annotation.
好的,那么使用 AOP 意味着什么,我可以指定何时执行一个带有特定注释的特定方法?是对的还是我遗漏了什么?
所以,如果前面的断言是正确的,这意味着(例如)我还可以指定类似的内容:"when it is performed a controller method annotated by @RequestMapping("/listAccounts")?(这意味着,当控制器处理对 /listAccounts 资源的 HttpRequest,执行如下操作:
execution(@RequestMapping("/listAccounts") * *(..))
我能不能做这样的事情?
Spring 正在使用 AspectJ 切入点表达式语言 (https://eclipse.org/aspectj/doc/next/adk15notebook/annotations-pointcuts-and-advice.html)
应用切入点表达式意味着拦截所有具有 'void' return 类型的名称和参数列表的方法调用,只要该方法用 @com.myapp.MyCustomAnnotation 注释。
尽管无法使用注释参数匹配连接点,因此您的第二个切入点表达式无效。
您不能像那样在切入点中显式指定注释的参数。相反,您可以设置一个切入点来捕获所有带有 @RequestMapping
注释的方法,然后从该方法中检索注释并检查该值是否与端点匹配。例如:
@PointCut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
@Before("requestMapping()")
public void handleRequestMapping(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String mapping = method.getAnnotation(RequestMapping.class).value()[0];
if (mapping.equals("/listAccounts") {
// do something
}
}
André R. 的回答是不正确的,因为 是 可以将注释匹配限制为参数值,但有一个限制:它只适用于简单类型,如字符串,整数和 类,而不是字符串数组。但在您的具体示例中,Spring 注释 @RequestMapping
的值类型是 String[]
,请参阅 Javadoc。让我们假设您遇到这种情况:
译注:
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int value();
String name() default "";
Class<?> type() default Object.class;
}
驱动申请:
这里我们有一个应用程序,其中包含多个方法,这些方法由 Spring 注释 @RequestMapping
和我们自制的注释 @MyAnnotation
:
注释
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
public class Application {
@RequestMapping("/listAccounts")
@MyAnnotation(11)
public void doSomething() {}
public void doSomethingElse() {}
@RequestMapping(value = {"/listAccounts","/another/method"}, name = "Newton")
@MyAnnotation(value = 22, type = String.class)
public void foo() {}
@RequestMapping(value = "/listAccounts", method = RequestMethod.POST, name = "Einstein")
@MyAnnotation(value = 11, name = "John Doe", type = String.class)
public void bar() {}
@RequestMapping(value = "/listCustomers", method = RequestMethod.GET, name = "Einstein")
@MyAnnotation(value = 22, name = "Jane Doe")
public void zot() {}
@RequestMapping(value = "/listAccounts", produces = {"application/json","application/xml"}, consumes = "text/html", name = "Newton")
public void baz() {}
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.doSomethingElse();
application.foo();
application.bar();
application.zot();
application.baz();
}
}
注解匹配方面:
package de.scrum_master.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.web.bind.annotation.RequestMapping;
import de.scrum_master.app.MyAnnotation;
@Aspect
public class AnnotationParameterMatcher {
// Match *all* methods annotated by @RequestMapping
@Before("execution(* *(..)) && @annotation(requestMapping)")
public void logRequestMapping(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
// Print joinpoint and annotation
System.out.println(thisJoinPoint + " -> " + requestMapping);
}
// Match *all* methods annotated by @RequestMapping
@Before("execution(* *(..)) && @annotation(requestMapping)")
public void logMatchingValue(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
// Only print if value array contains "/listAccounts"
if (Arrays.asList(requestMapping.value()).contains("/listAccounts"))
System.out.println(" value contains '/listAccounts'");
}
// Match *all* methods annotated by @RequestMapping and bind 'name' parameter
@Before("execution(* *(..)) && @annotation(RequestMapping(name))")
public void logName(JoinPoint thisJoinPoint, String name) {
System.out.println(" name = '" + name + "'");
}
// Match methods annotated by @MyAnnotation with value=11
@Before("execution(@MyAnnotation(value=11) * *(..))")
public void logName(JoinPoint thisJoinPoint) {
System.out.println(" @MyAnnotation(value=11) detected");
}
// Match methods annotated by @MyAnnotation with 3 specific parameter values
@Before("execution(@MyAnnotation(value=11, name=\"John Doe\", type=String.class) * *(..)) && @annotation(myAnnotation)")
public void logName(JoinPoint thisJoinPoint, MyAnnotation myAnnotation) {
System.out.println(" " + myAnnotation);
}
}
控制台输出:
execution(void de.scrum_master.app.Application.doSomething()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=, produces=[], params=[], value=[/listAccounts], consumes=[])
value contains '/listAccounts'
name = ''
@MyAnnotation(value=11) detected
execution(void de.scrum_master.app.Application.foo()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=Newton, produces=[], params=[], value=[/listAccounts, /another/method], consumes=[])
value contains '/listAccounts'
name = 'Newton'
execution(void de.scrum_master.app.Application.bar()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[POST], name=Einstein, produces=[], params=[], value=[/listAccounts], consumes=[])
value contains '/listAccounts'
name = 'Einstein'
@MyAnnotation(value=11) detected
@de.scrum_master.app.MyAnnotation(name=John Doe, type=class java.lang.String, value=11)
execution(void de.scrum_master.app.Application.zot()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[GET], name=Einstein, produces=[], params=[], value=[/listCustomers], consumes=[])
name = 'Einstein'
execution(void de.scrum_master.app.Application.baz()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=Newton, produces=[application/json, application/xml], params=[], value=[/listAccounts], consumes=[text/html])
value contains '/listAccounts'
name = 'Newton'
我正在学习 Spring AOP 并且我有以下关于我学习中发现的问题的疑问 material.
因此请考虑以下切入点:execution(@com.myapp.MyCustomAnnotation void *(..))。具体是什么意思?
它给了我以下答案,在我看来有点奇怪(使用我对 AOP 工作原理的了解)。它说:
This will select joint points representing voiud method that are annotated by @com.myapp.MyCustomAnnotation annotation.
好的,那么使用 AOP 意味着什么,我可以指定何时执行一个带有特定注释的特定方法?是对的还是我遗漏了什么?
所以,如果前面的断言是正确的,这意味着(例如)我还可以指定类似的内容:"when it is performed a controller method annotated by @RequestMapping("/listAccounts")?(这意味着,当控制器处理对 /listAccounts 资源的 HttpRequest,执行如下操作:
execution(@RequestMapping("/listAccounts") * *(..))
我能不能做这样的事情?
Spring 正在使用 AspectJ 切入点表达式语言 (https://eclipse.org/aspectj/doc/next/adk15notebook/annotations-pointcuts-and-advice.html)
应用切入点表达式意味着拦截所有具有 'void' return 类型的名称和参数列表的方法调用,只要该方法用 @com.myapp.MyCustomAnnotation 注释。
尽管无法使用注释参数匹配连接点,因此您的第二个切入点表达式无效。
您不能像那样在切入点中显式指定注释的参数。相反,您可以设置一个切入点来捕获所有带有 @RequestMapping
注释的方法,然后从该方法中检索注释并检查该值是否与端点匹配。例如:
@PointCut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
@Before("requestMapping()")
public void handleRequestMapping(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String mapping = method.getAnnotation(RequestMapping.class).value()[0];
if (mapping.equals("/listAccounts") {
// do something
}
}
André R. 的回答是不正确的,因为 是 可以将注释匹配限制为参数值,但有一个限制:它只适用于简单类型,如字符串,整数和 类,而不是字符串数组。但在您的具体示例中,Spring 注释 @RequestMapping
的值类型是 String[]
,请参阅 Javadoc。让我们假设您遇到这种情况:
译注:
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int value();
String name() default "";
Class<?> type() default Object.class;
}
驱动申请:
这里我们有一个应用程序,其中包含多个方法,这些方法由 Spring 注释 @RequestMapping
和我们自制的注释 @MyAnnotation
:
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
public class Application {
@RequestMapping("/listAccounts")
@MyAnnotation(11)
public void doSomething() {}
public void doSomethingElse() {}
@RequestMapping(value = {"/listAccounts","/another/method"}, name = "Newton")
@MyAnnotation(value = 22, type = String.class)
public void foo() {}
@RequestMapping(value = "/listAccounts", method = RequestMethod.POST, name = "Einstein")
@MyAnnotation(value = 11, name = "John Doe", type = String.class)
public void bar() {}
@RequestMapping(value = "/listCustomers", method = RequestMethod.GET, name = "Einstein")
@MyAnnotation(value = 22, name = "Jane Doe")
public void zot() {}
@RequestMapping(value = "/listAccounts", produces = {"application/json","application/xml"}, consumes = "text/html", name = "Newton")
public void baz() {}
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.doSomethingElse();
application.foo();
application.bar();
application.zot();
application.baz();
}
}
注解匹配方面:
package de.scrum_master.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.web.bind.annotation.RequestMapping;
import de.scrum_master.app.MyAnnotation;
@Aspect
public class AnnotationParameterMatcher {
// Match *all* methods annotated by @RequestMapping
@Before("execution(* *(..)) && @annotation(requestMapping)")
public void logRequestMapping(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
// Print joinpoint and annotation
System.out.println(thisJoinPoint + " -> " + requestMapping);
}
// Match *all* methods annotated by @RequestMapping
@Before("execution(* *(..)) && @annotation(requestMapping)")
public void logMatchingValue(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
// Only print if value array contains "/listAccounts"
if (Arrays.asList(requestMapping.value()).contains("/listAccounts"))
System.out.println(" value contains '/listAccounts'");
}
// Match *all* methods annotated by @RequestMapping and bind 'name' parameter
@Before("execution(* *(..)) && @annotation(RequestMapping(name))")
public void logName(JoinPoint thisJoinPoint, String name) {
System.out.println(" name = '" + name + "'");
}
// Match methods annotated by @MyAnnotation with value=11
@Before("execution(@MyAnnotation(value=11) * *(..))")
public void logName(JoinPoint thisJoinPoint) {
System.out.println(" @MyAnnotation(value=11) detected");
}
// Match methods annotated by @MyAnnotation with 3 specific parameter values
@Before("execution(@MyAnnotation(value=11, name=\"John Doe\", type=String.class) * *(..)) && @annotation(myAnnotation)")
public void logName(JoinPoint thisJoinPoint, MyAnnotation myAnnotation) {
System.out.println(" " + myAnnotation);
}
}
控制台输出:
execution(void de.scrum_master.app.Application.doSomething()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=, produces=[], params=[], value=[/listAccounts], consumes=[])
value contains '/listAccounts'
name = ''
@MyAnnotation(value=11) detected
execution(void de.scrum_master.app.Application.foo()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=Newton, produces=[], params=[], value=[/listAccounts, /another/method], consumes=[])
value contains '/listAccounts'
name = 'Newton'
execution(void de.scrum_master.app.Application.bar()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[POST], name=Einstein, produces=[], params=[], value=[/listAccounts], consumes=[])
value contains '/listAccounts'
name = 'Einstein'
@MyAnnotation(value=11) detected
@de.scrum_master.app.MyAnnotation(name=John Doe, type=class java.lang.String, value=11)
execution(void de.scrum_master.app.Application.zot()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[GET], name=Einstein, produces=[], params=[], value=[/listCustomers], consumes=[])
name = 'Einstein'
execution(void de.scrum_master.app.Application.baz()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=Newton, produces=[application/json, application/xml], params=[], value=[/listAccounts], consumes=[text/html])
value contains '/listAccounts'
name = 'Newton'