英文:
AspectJ: Pointcut to declare and retrieve an annotation of a method's parameter
问题
我已阅读以下有价值的链接:
考虑以下对setter方法的请求
public void setSomething(@ParameterLevel(name="abc") String something){
this.something = something;
}
我已经有以下内容并且运行良好:
@Pointcut("execution(* *.*(@somepackage.ParameterLevel (*)))")
void parameterLevel01() {}
现在我想通过方法的参数来获取@ParameterLevel
注解,如下所示:
@Pointcut("execution(* *.*(@somepackage.ParameterLevel (*)))")
void parameterLevel01(ParameterLevel parameterLevel) {} //<--要直接在advice方法中使用
目的是在advice方法中直接使用注解作为参数,类似于:
对于@ClassLevel
在类中使用@within(classLevel)
:
@ClassLevel
public class SomeClass {
...
}
对于@MethodLevel
在方法中使用@annotation(methodLevel)
:
@MethodLevel
public void somethingToDo(){
...
}
如何实现这个目标。是否可能?我正在使用AspectJ 1.9.6版本。
英文:
I have read the following valuable links:
- Spring AOP pointcut for annotated argument
- How to write an Aspect pointcut based on an annotated parameter
- AspectJ pointcut expression match parameter annotations at any position
Consider this request for a setter method
public void setSomething(@ParameterLevel(name="abc") String something){
this.something = something;
}
I have the following and works fine:
@Pointcut("execution(* *.*(@somepackage.ParameterLevel (*)))")
void parameterLevel01() {}
Now I want retrieve the @ParameterLevel
annotation through a method's parameter such as the following:
@Pointcut("execution(* *.*(@somepackage.ParameterLevel (*)))")
void parameterLevel01(ParameterLevel parameterLevel) {} <--To be used directly in the advice method
The purpose is use the Annotation directly how a parameter in the advice method
Something similar such as:
@within(classLevel)
for @ClassLevel
in:
@ClassLevel
public class SomeClass {
...
}
@annotation(methodLevel)
for @MethodLevel
in:
@MethodLevel
public void somethingToDo(){
...
}
How accomplish this goal. Is possible? I am working with AspectJ 1.9.6
答案1
得分: 1
无论您使用 .., @MyAnnotation (*), ..
还是仅使用 @MyAnnotation (*)
,这只是消除了可能存在的多个匹配的歧义,但没有直接的方法将方法参数注释绑定到通知参数,只能绑定方法参数本身。在AspectJ中,这一点并没有改变。如果有变化的话,在发布说明中应该会提到,因为那将是一个新功能。
因此,您将不得不使用我在您的问题中已经链接到的另外两个答案中的方法,即手动迭代参数类型和注释。
有点不相关,有一个非常旧的 Bugzilla 票号 #233718,关于绑定多个匹配的(带注释的)参数,但不是关于绑定它们的注释。最近我与AspectJ的维护者Andy Clement的一次讨论 中提到了这个问题。但是,即使有一天实现了这个功能,也不会解决您的问题。
我认为您可以从这里开始,并根据链接的问题调整我的解决方案以满足您的需求。如果您对此有任何后续问题,请随时告诉我,但这应该相当简单。如果您感觉有兴趣,您可能能够优化,因为您知道确切的参数位置(考虑数组索引),这样您就不需要遍历所有参数。
更新: 这里有一个小的 MCVE(最小可复现示例)供您参考。它基于 这个答案,并已经被简化为假定注释总是在第一个参数上,且仅在第一个参数上。
标记注释 + 驱动程序应用:
package de.scrum_master.app;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
@Retention(RUNTIME)
public @interface ParameterLevel {
String name();
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new Application().doSomething("foo");
}
public void doSomething(@ParameterLevel(name="abc") String string) {}
}
切面:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.SoftException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import de.scrum_master.app.ParameterLevel;
@Aspect
public class ParameterLevelAspect {
@Before("execution(public * *(@de.scrum_master.app.ParameterLevel (*))) && args(string)")
public void beforeAdvice(JoinPoint thisJoinPoint, String string) {
System.out.println(thisJoinPoint + " -> " + string);
MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature();
String methodName = signature.getMethod().getName();
Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();
Annotation[] annotations;
try {
annotations = thisJoinPoint.getTarget().getClass()
.getMethod(methodName, parameterTypes)
.getParameterAnnotations()[0];
} catch (NoSuchMethodException | SecurityException e) {
throw new SoftException(e);
}
ParameterLevel parameterLevel = null;
for (Annotation annotation : annotations) {
if (annotation.annotationType() == ParameterLevel.class) {
parameterLevel = (ParameterLevel) annotation;
break;
}
}
assert parameterLevel != null;
System.out.println(" " + parameterLevel + " -> " + parameterLevel.name());
}
}
控制台日志:
execution(void de.scrum_master.app.Application.doSomething(String)) -> foo
@de.scrum_master.app.ParameterLevel(name="abc") -> abc
英文:
No matter if you use .., @MyAnnotation (*), ..
or just @MyAnnotation (*)
, which only removes the ambiguity of possibly multiple matches, there is no direct way to bind a method argument annotation to an advice argument, only the method argument itself. This has not changed in AspectJ. You would have seen it mentioned in the release notes otherwise, because it would be a new feature.
So you will have to use the method from my other two answers which you have already linked to in your question, i.e. iterating over parameter types and annotations manually.
Somewhat off-topic, there is a very old Bugzilla ticket #233718 which is about binding multiple matched (annotated) parameters, but not about binding their annotations. It came up in a recent discussion I had with AspectJ maintainer Andy Clement. But even if this was implemented one day, it would not solve your problem.
I think you can take it from here and adapt my solution from the linked questions to your needs. Feel free to let me know if you have any follow-up questions about that, but it should be pretty straightforward. You might be able to optimise because you know the exact parameter position (think array index), if you feel so inclined, i.e. you don't need to iterate over all parameters.
Update: Here is a little MCVE for you. It is based on this answer and has been simplified to assume the annotation is always on the first parameter and the first parameter only.
Please learn what an MCVE is and provide one by yourself next time because it is your job, not mine. This was your free shot.
Marker annotation + driver application:
<!-- language: java -->
package de.scrum_master.app;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
@Retention(RUNTIME)
public @interface ParameterLevel {
String name();
}
<!-- language: java -->
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new Application().doSomething("foo");
}
public void doSomething(@ParameterLevel(name="abc") String string) {}
}
Aspect:
<!-- language: java -->
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.SoftException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import de.scrum_master.app.ParameterLevel;
@Aspect
public class ParameterLevelAspect {
@Before("execution(public * *(@de.scrum_master.app.ParameterLevel (*))) && args(string)")
public void beforeAdvice(JoinPoint thisJoinPoint, String string) {
System.out.println(thisJoinPoint + " -> " + string);
MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature();
String methodName = signature.getMethod().getName();
Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();
Annotation[] annotations;
try {
annotations = thisJoinPoint.getTarget().getClass()
.getMethod(methodName, parameterTypes)
.getParameterAnnotations()[0];
} catch (NoSuchMethodException | SecurityException e) {
throw new SoftException(e);
}
ParameterLevel parameterLevel = null;
for (Annotation annotation : annotations) {
if (annotation.annotationType() == ParameterLevel.class) {
parameterLevel = (ParameterLevel) annotation;
break;
}
}
assert parameterLevel != null;
System.out.println(" " + parameterLevel + " -> " + parameterLevel.name());
}
}
Console log:
execution(void de.scrum_master.app.Application.doSomething(String)) -> foo
@de.scrum_master.app.ParameterLevel(name="abc") -> abc
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论