我正在将一些使用Spring AOP的代码迁移到AspectJ方面(在编译时编织)。我正在寻找关于如何修改切入点以使它们在迁移后表现相同的反馈?
这让我很头疼,我想知道我是否可以更改切入点,使其仍然表现得好像它仍然是一个代理?(即在类型层次结构中的任何一点排除来自自身内部的调用,例如调用继承的函数)
我认为Jigish混合方面样式的想法是一种很好的迁移方法,甚至是永久地继续使用Spring的方法,只要您没有任何迫在眉睫的问题(例如性能)。
现在,说到这里,我为您提供了一个变通方法,以防您出于任何原因需要在全面AspectJ中排除内部调用。我不认为它是优雅的,但它的工作。这里有一个概念证明:
两个示例应用程序类:
package de.scrum_master.app;
public class Foo {
public void doSomethingFooish() {
fooTwo(22);
}
public void fooOne(Bar bar) {
bar.doSomethingBarish();
fooTwo(11);
}
public String fooTwo(int number) {
return fooThree("xxx");
}
public String fooThree(String text) {
return text + " " + text + " " + text;
}
}
package de.scrum_master.app;
public class Bar {
public void doSomethingBarish() {
barTwo(22);
}
public void barOne(Foo foo) {
foo.doSomethingFooish();
barTwo(11);
}
public String barTwo(int number) {
return barThree("xxx");
}
public String barThree(String text) {
return text + " " + text + " " + text;
}
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Foo foo = new Foo();
Bar bar = new Bar();
foo.fooOne(bar);
bar.barOne(foo);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SampleAspect {
@Pointcut("call(public * de.scrum_master..*(..)) && !within(SampleAspect)")
public static void publicMethodCalls() {}
@Before("publicMethodCalls()")
public void myPointcut(
JoinPoint thisJoinPoint,
JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart
) {
System.out.println(thisEnclosingJoinPointStaticPart + " -> " + thisJoinPoint);
}
}
application
同时调用foo
和bar
方法,foo
调用bar
方法,但也在内部调用自己的方法,bar
调用foo
方法,但也在内部调用自己的方法。execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Foo.fooOne(Bar))
execution(void de.scrum_master.app.Foo.fooOne(Bar)) -> call(void de.scrum_master.app.Bar.doSomethingBarish())
execution(void de.scrum_master.app.Bar.doSomethingBarish()) -> call(String de.scrum_master.app.Bar.barTwo(int))
execution(String de.scrum_master.app.Bar.barTwo(int)) -> call(String de.scrum_master.app.Bar.barThree(String))
execution(void de.scrum_master.app.Foo.fooOne(Bar)) -> call(String de.scrum_master.app.Foo.fooTwo(int))
execution(String de.scrum_master.app.Foo.fooTwo(int)) -> call(String de.scrum_master.app.Foo.fooThree(String))
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Bar.barOne(Foo))
execution(void de.scrum_master.app.Bar.barOne(Foo)) -> call(void de.scrum_master.app.Foo.doSomethingFooish())
execution(void de.scrum_master.app.Foo.doSomethingFooish()) -> call(String de.scrum_master.app.Foo.fooTwo(int))
execution(String de.scrum_master.app.Foo.fooTwo(int)) -> call(String de.scrum_master.app.Foo.fooThree(String))
execution(void de.scrum_master.app.Bar.barOne(Foo)) -> call(String de.scrum_master.app.Bar.barTwo(int))
execution(String de.scrum_master.app.Bar.barTwo(int)) -> call(String de.scrum_master.app.Bar.barThree(String))
改进的动态排除内部调用的方面:
现在我们需要比较调用方(this()
切入点在本机aspectJ语法中)是否与被调用方(target()
切入点)相同。如果是这样,我们希望跳过建议执行。在AspectJ中有两种获取调用方/被调用方引用的方法:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SampleAspect {
@Pointcut("call(public * de.scrum_master..*(..)) && !within(SampleAspect)")
public static void publicMethodCalls() {}
@Before("publicMethodCalls() && target(callee)")
public void myPointcut(
Object callee,
JoinPoint thisJoinPoint,
JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart
) {
Object caller = thisJoinPoint.getThis();
if (caller == callee)
return;
System.out.println(thisEnclosingJoinPointStaticPart + " -> " + thisJoinPoint);
System.out.println(" caller = " + caller);
System.out.println(" callee = " + callee);
}
}
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Foo.fooOne(Bar))
caller = null
callee = de.scrum_master.app.Foo@6a5c2445
execution(void de.scrum_master.app.Foo.fooOne(Bar)) -> call(void de.scrum_master.app.Bar.doSomethingBarish())
caller = de.scrum_master.app.Foo@6a5c2445
callee = de.scrum_master.app.Bar@47516490
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Bar.barOne(Foo))
caller = null
callee = de.scrum_master.app.Bar@47516490
execution(void de.scrum_master.app.Bar.barOne(Foo)) -> call(void de.scrum_master.app.Foo.doSomethingFooish())
caller = de.scrum_master.app.Bar@47516490
callee = de.scrum_master.app.Foo@6a5c2445
在特殊情况下,如果您知道某个建议所针对的确切类名,那么您可能可以使用cflow()
来排除内部调用,而不会构造出难看的If
结构,但我没有考虑清楚,也没有尝试过。它在一般情况下不起作用。
更新1:
我又玩了一些。下面是使用execution()
代替call()
的替代方案。因此,它不能依赖于封闭的连接点,而需要分析当前的调用堆栈。我没有对照上面描述的解决方案对性能进行基准测试,但它肯定会编织更少的连接点。此外,它在其切入点中使用if()
,而不是在通知中使用if
语句。条件仍然是在运行时动态确定的,而不是在编织代码时静态确定的,但我猜在这里无论如何都是不可能的,因为它依赖于控制流。
package de.scrum_master.aspect;
public aspect DemoAspect {
pointcut publicMethodCalls() :
execution(public !static * de.scrum_master..*(..)) &&
if(Thread.currentThread().getStackTrace()[3].getClassName() != thisJoinPointStaticPart.getSignature().getDeclaringTypeName());
before() : publicMethodCalls() {
System.out.println(thisJoinPoint);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SampleAspect {
@Pointcut("execution(public !static * de.scrum_master..*(..)) && if()")
public static boolean publicMethodCalls(JoinPoint thisJoinPoint) {
return Thread.currentThread().getStackTrace()[3].getClassName() != thisJoinPoint.getSignature().getDeclaringTypeName();
}
@Before("publicMethodCalls(thisJoinPoint)")
public void myPointcut(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
execution(void de.scrum_master.app.Foo.fooOne(Bar))
execution(void de.scrum_master.app.Bar.doSomethingBarish())
execution(void de.scrum_master.app.Bar.barOne(Foo))
execution(void de.scrum_master.app.Foo.doSomethingFooish())
更新2:
下面是使用execution()
加上通过Class.IsAssignableFrom(Class)
的一些反射的另一个变体,它也应该与类层次结构和接口一起工作:
package de.scrum_master.aspect;
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.annotation.Pointcut;
@Aspect
public class SampleAspect {
@Pointcut("execution(public !static * de.scrum_master..*(..)) && target(callee) && if()")
public static boolean publicMethodCalls(Object callee, JoinPoint thisJoinPoint) {
Class<?> callerClass;
try {
callerClass = Class.forName(
Thread.currentThread().getStackTrace()[3].getClassName()
);
} catch (Exception e) {
throw new SoftException(e);
}
Class<?> calleeClass = callee.getClass();
return !callerClass.isAssignableFrom(calleeClass);
}
@Before("publicMethodCalls(callee, thisJoinPoint)")
public void myPointcut(Object callee, JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
Java+Spring+Maven应用程序: 有人能给我提供链接或者建议我一个纯AspectJ实现,不使用基于代理的Spring AOP吗? 如果我试图从同一中的访问,则不支持此操作。 我想知道:1)如何用切入点编写一个支持类内方法调用的aspectj?2)如何将其配置到我当前的Spring,maven项目中,使用aspectj加载时编织?3)如何配置aspectj maven插件,以便在Tomc
我是一个新的bee-to-Spring框架,我指的是Spring项目中可用的文档。 在这个过程中,我还学习了一个新概念AOP。我遵循spring文档来尝试一些示例http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html 我尝试使用Spring AOP为我的第一个Aspect Hellowor
主要内容:读者,前提条件,Spring AOP 概述Spring框架的关键组件之一是面向方面编程(AOP)框架。 面向方面的编程需要将程序逻辑分解成不同的部分。 此教程将通过简单实用的方法来学习Spring框架提供的AOP/面向方面编程。 读者 本教程主要是为Spring 面向方面编程(AOP)初学者准备的,帮助他们了解与Spring的AOP框架相关的基础到高级概念。 前提条件 在开始练习本教程系列文章中给出的各种类型的示例之前,我们假设您已经了解
我有一个自定义注释, 我正在将这个注释用于以下方法, 我在以下方面捕捉事件, @Around建议仅适用于“进程连接点”参数。如果将 XAudit 作为第二个参数传递,则会引发以下错误: 我需要在方面中使用xaud才能访问Xaud的操作。请分享一些关于如何在@周围方面中访问@Xaud值的指针。
我将获得一个AWS S3图像资源作为S3ObjectInputStream,并希望将其放入Vaadin图像HTML组件中。Vaadin图像只有两种初始化方式。第一个是通过一个表示文件路径的字符串,这意味着将S3映像写入磁盘,由于数据安全原因,我无法这样做,第二个是通过实现“AbstractStreamResource”的东西。现在的问题是,如何将S3ObjectInputStream转换为实现“A
问题内容: 我希望将标准JSON对象处理为一个对象,其中每行必须包含一个单独的,自包含的有效JSON对象。查看JSON行 : 我当前的解决方案是将JSON文件读取为文本文件,并从开头和结尾删除。因此,在每行上创建一个有效的JSON对象,而不是包含行的嵌套对象。 我想知道是否有更优雅的解决方案?我怀疑在文件上使用字符串操作可能会出错。 目的是在Spark上将文件读入RDD。查看相关问题-使用Apac