使用Aspectj调用joinPoint.getArgs时如何忽略带有javax注释的参数?
How to ignore parameters with javax annotation when calling joinPoint.getArgs using Aspectj?
我的函数包含不同的 javax 查询注释,例如:@QueryParam
、@Context
、@PathParam
等。
有没有办法在调用 joinPoint.getArgs() 时排除这些参数?
示例:
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("{pathParam}/v1/{pathParam2}/")
@MyAnnotation
public Response update(@PathParam("pathParam") String p1, @PathParam("pathParam2") int p2, MyObject x);
@Before("@annotation(MyAnnotation)")
public void doSomething(JoinPoint joinPoint){
Object[] objects = joinPoint.getArgs(); // HERE - is there a way to get only MyObject and not other params..?
}
我想要这样做的原因是我有几个 url,同时将 ~10% 标记为持久。这意味着我希望将输入数据保存在某个持久服务中。查询和上下文参数对我来说并不重要,但输入数据本身很重要。
我不认为有什么神奇的方法可以做到这一点,所以选择显而易见的方法:
- 定义你的论点接受标准;
- 遍历 args 并根据先前定义的条件进行过滤,仅此而已。
看来您的验收标准是 arg 没有用这些 javax 注释进行注释,对吗?
试试这个:
Object[] args = joinPoint.getArgs();
Annotation[][] anns = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterAnnotations();
for (int i = 0; i < args.length; i++) {
for (int j = 0; j < args[i].length; j++) {
// check here for the annotations you would like to exclude
}
}
假设你真的使用 full AspectJ 而不是 Spring AOP 像很多其他人一样,你应该意识到在 full AspectJ @annotation(XY)
可能不仅匹配 execution()
个连接点,而且还匹配 call()
,即您的建议将被触发两次。更糟糕的是,如果除了方法执行之外的其他地方也被注释了——例如classes, fields, constructors, parameters - 切入点也将匹配并且您尝试转换为 MethodSignature
将因此导致异常。
此外,请注意,在@AspectJ 语法中,您需要提供要匹配的注解的完全限定 class 名称,即不要忘记在包名称前加上前缀。否则根本就没有比赛。因此,在执行任何其他操作之前,您需要将切入点更改为:
@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))
现在这是一个完全自洽的例子,SSCCE 产生可重复的结果,正如我在你的问题下的评论中所要求的:
译注:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
驱动申请:
如您所见,测试方法的参数带有不同类型的注解:
- 仅 javax 注释
- javax + 自带注解
- 只有你自己的注释
- 无注释
我们要忽略#1/2,只打印#3/4。
package de.scrum_master.app;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
public class Application {
public static void main(String[] args) {
new Application().update("foo", 11, "bar", 22);
}
@MyAnnotation
public Response update(
@PathParam("pathParam") String p1,
@PathParam("pathParam2") @MyAnnotation int p2,
@MyAnnotation String text,
int number
) {
return null;
}
}
看点:
正如用户 Andre Paschoal 开始在他的代码片段中展示的那样,您需要遍历参数和注释数组才能实现过滤技巧。我认为它非常丑陋,而且可能只是为了日志记录而变慢(我认为这是你想要做的),但对于它的价值,这是你的解决方案:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect
public class ParameterFilterAspect {
@Before("@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))")
public void doSomething(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
for (int i = 0; i < args.length; i++) {
boolean hasJavaxAnnotation = false;
for (Annotation annotation : annotationMatrix[i]) {
if (annotation.annotationType().getPackage().getName().startsWith("javax.")) {
hasJavaxAnnotation = true;
break;
}
}
if (!hasJavaxAnnotation)
System.out.println(args[i]);
}
}
}
控制台日志:
bar
22
太棒了! :-)
这段代码适合我:
Annotation[][] anns = ((MethodSignature) thisJoinPoint.getSignature()).getMethod().getParameterAnnotations();
parameterValues = thisJoinPoint.getArgs();
signature = (MethodSignature) thisJoinPoint.getSignature();
parameterNames = signature.getParameterNames();
if (parameterValues != null) {
for (int i = 0; i < parameterValues.length; i++) {
boolean shouldBeExcluded = false;
for (Annotation annotation : anns[i]) {
if (annotation instanceof ExcludeFromCustomLogging) {//<<---------ExcludeFromCustomLogging is my class
shouldBeExcluded = true;
break;
}
}
if (shouldBeExcluded) {
//System.out.println("should be excluded===>"+parameterNames[i]);
continue;
}
//.......and your business
}
我的函数包含不同的 javax 查询注释,例如:@QueryParam
、@Context
、@PathParam
等。
有没有办法在调用 joinPoint.getArgs() 时排除这些参数?
示例:
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("{pathParam}/v1/{pathParam2}/")
@MyAnnotation
public Response update(@PathParam("pathParam") String p1, @PathParam("pathParam2") int p2, MyObject x);
@Before("@annotation(MyAnnotation)")
public void doSomething(JoinPoint joinPoint){
Object[] objects = joinPoint.getArgs(); // HERE - is there a way to get only MyObject and not other params..?
}
我想要这样做的原因是我有几个 url,同时将 ~10% 标记为持久。这意味着我希望将输入数据保存在某个持久服务中。查询和上下文参数对我来说并不重要,但输入数据本身很重要。
我不认为有什么神奇的方法可以做到这一点,所以选择显而易见的方法:
- 定义你的论点接受标准;
- 遍历 args 并根据先前定义的条件进行过滤,仅此而已。
看来您的验收标准是 arg 没有用这些 javax 注释进行注释,对吗?
试试这个:
Object[] args = joinPoint.getArgs();
Annotation[][] anns = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterAnnotations();
for (int i = 0; i < args.length; i++) {
for (int j = 0; j < args[i].length; j++) {
// check here for the annotations you would like to exclude
}
}
假设你真的使用 full AspectJ 而不是 Spring AOP 像很多其他人一样,你应该意识到在 full AspectJ @annotation(XY)
可能不仅匹配 execution()
个连接点,而且还匹配 call()
,即您的建议将被触发两次。更糟糕的是,如果除了方法执行之外的其他地方也被注释了——例如classes, fields, constructors, parameters - 切入点也将匹配并且您尝试转换为 MethodSignature
将因此导致异常。
此外,请注意,在@AspectJ 语法中,您需要提供要匹配的注解的完全限定 class 名称,即不要忘记在包名称前加上前缀。否则根本就没有比赛。因此,在执行任何其他操作之前,您需要将切入点更改为:
@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))
现在这是一个完全自洽的例子,SSCCE 产生可重复的结果,正如我在你的问题下的评论中所要求的:
译注:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
驱动申请:
如您所见,测试方法的参数带有不同类型的注解:
- 仅 javax 注释
- javax + 自带注解
- 只有你自己的注释
- 无注释
我们要忽略#1/2,只打印#3/4。
package de.scrum_master.app;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
public class Application {
public static void main(String[] args) {
new Application().update("foo", 11, "bar", 22);
}
@MyAnnotation
public Response update(
@PathParam("pathParam") String p1,
@PathParam("pathParam2") @MyAnnotation int p2,
@MyAnnotation String text,
int number
) {
return null;
}
}
看点:
正如用户 Andre Paschoal 开始在他的代码片段中展示的那样,您需要遍历参数和注释数组才能实现过滤技巧。我认为它非常丑陋,而且可能只是为了日志记录而变慢(我认为这是你想要做的),但对于它的价值,这是你的解决方案:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect
public class ParameterFilterAspect {
@Before("@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))")
public void doSomething(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
for (int i = 0; i < args.length; i++) {
boolean hasJavaxAnnotation = false;
for (Annotation annotation : annotationMatrix[i]) {
if (annotation.annotationType().getPackage().getName().startsWith("javax.")) {
hasJavaxAnnotation = true;
break;
}
}
if (!hasJavaxAnnotation)
System.out.println(args[i]);
}
}
}
控制台日志:
bar
22
太棒了! :-)
这段代码适合我:
Annotation[][] anns = ((MethodSignature) thisJoinPoint.getSignature()).getMethod().getParameterAnnotations();
parameterValues = thisJoinPoint.getArgs();
signature = (MethodSignature) thisJoinPoint.getSignature();
parameterNames = signature.getParameterNames();
if (parameterValues != null) {
for (int i = 0; i < parameterValues.length; i++) {
boolean shouldBeExcluded = false;
for (Annotation annotation : anns[i]) {
if (annotation instanceof ExcludeFromCustomLogging) {//<<---------ExcludeFromCustomLogging is my class
shouldBeExcluded = true;
break;
}
}
if (shouldBeExcluded) {
//System.out.println("should be excluded===>"+parameterNames[i]);
continue;
}
//.......and your business
}