?前两篇AspectJ入门的文章大致的介绍了AspectJ,本文更完整、更详细的介绍AspectJ的基础知识。包括了切点、连接点、类型间声明及thisJoinPoint的基础知识,来更好的理解AspectJ的语法。
挑选切点的时候,我们还可以通过方法的访问权限、是否是static来区分,同时也能挑选出接口的方法:
public aspect PointPickAspect {
??? pointcut staticPoint(): call(static * article.service.PointPick.*(..));
??? pointcut privatePoint(): call(private * article.service.PointPick.*(..));
??? pointcut interfacePoint(): call(* article.service.CustomInterface.*(..)); // CustomInterface是接口
??? before(): staticPoint() || privatePoint() || interfacePoint() {
??????? System.out.println();
??????? System.out.println(thisJoinPoint.toLongString());
??? }
}
call:获取调用签名时的连接点。在实际开发中会用到。
execution:获取代码段在运行时的连接点。通常用于跟踪调试。
call 与 execution对于within 或withincode的匹配方式也不同。
public class ExecutionAndCall {
static void fun(int num) {
System.out.println(num);
System.out.println();
if (num > 0) fun(--num);
}
public static void main(String[] args) {
ExecutionAndCall.fun(4);
}
}
public aspect ExecutionAndCallAspect {
pointcut callPoint(): call(void article.service.ExecutionAndCall.fun(..)) &&
withincode(void article.service.ExecutionAndCall.fun(..)); // 切点是:fun中递归调用的fun连接点
pointcut executionPoint(): execution(void article.service.ExecutionAndCall.fun(..));
// 注意execution 的连接点严格意义来说不在fun方法体内,所以加上:
// && withincode(void article.service.ExecutionAndCall.fun(..)) 将匹配不到任何连接点
before(): callPoint() {
System.out.println("-----callPoint-----");
System.out.println(thisJoinPointStaticPart.getSignature());
System.out.println(thisJoinPointStaticPart.getSourceLocation());
}
before(): executionPoint() {
System.out.println("----executionPoint----");
System.out.println(thisJoinPointStaticPart.getSignature());
System.out.println(thisJoinPointStaticPart.getSourceLocation());
}
}
分类指示符 | 选择连接点的特定种类,如:call、execution、hander、get、set |
作用域指示符 | 选择一组连接点。with及withincode |
上下文指示符 | 基于上下文匹配,可选择地绑定的指示符。如 this、target、args、@annotation |
表 切点类型
作用域指示符匹配速度非常快,它们可以非常快速地消除不应该进一步处理的连接点组。
所以好的切点标准是,至少包括作用域指示符和分类指示符。
AspectJ 可以为类声明及定义成员(字段、方法和构造器),还可以为类实现新的接口或者继承新的类型。
在AspectJ 中为类定义的成员,如果其访问权限运行,可以在类中及切面中使用。
private | 只限于当前切面访问(被定义的类中也不能访问)。就是在其他切面或这个类也定义了同名的属性,也不会互相干扰。 |
default | 包访问权限。在这个切面所在包下的切面及类可以访问。可能会发生同名冲突。 |
public | 任何切面或者类都可以访问,可能会发生同名冲突。 |
表 Aspect类型间声明支持的访问权限
还可以定义类新的构造器。
public class InterEntityParent {
void sayByFather() {
System.out.println("InterEntityParent 父级");
}
}
public class InterEntity {
private int x = 0;
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public static void main(String[] args) {
InterEntity interEntity = new InterEntity(99); // 当前类并未定义该构造器,但是在切面中定义了
interEntity.show();
// interEntity.sayByFather(); // 编码时虽红字提示,但是能运行,在切面中为该类继承了InterEntityParent
}
// 运行结果:
// article.service.InterEntity@2a84aee7.x:99
// 同时可以访问到同包下,为该类定义的default访问类型变量:InterEntityAspect定义的属性
// InterEntityParent 父级
}
public aspect InterEntityAspect {
interface InterEntityAspectInterface {
void show();
}
private int InterEntity.x = 99; // InterEntity 中已存在x,但这里定义private并不会冲突
String InterEntity.defaultVal = "InterEntityAspect定义的属性"; // 在当前切面所在包下的任何切面或类可以访问
declare parents: InterEntity implements InterEntityAspectInterface; // 在InterEntity中或者切面中必须为其实现show方法,否则编译错误
declare parents: InterEntity extends InterEntityParent; // 让InterEntity继承InterEntityParent
public InterEntity.new(int num1) { // 定义InterEntity 构造器
this.setX(num1); // this 指的是InterEntity的实例
}
}
public aspect InterEntityAspect2 {
public void InterEntity.show() { // 为InterEntity定义show方法
System.out.println(this + ".x:" + this.getX());
System.out.println("同时可以访问到同包下,为该类定义的default访问类型变量:" + this.defaultVal);
}
}
封闭连接点的静态部分。不是当前连接点,是封闭连接点。可以使用这个来打印出调用的原位置。
public class EnclosingJoinPointEntity {
public void fun1() { // enclosePoint 连接点的封闭节点
System.out.println("fun1");
System.out.println("---");
fun2(); // enclosePoint 的连接点
}
public void fun2() {
System.out.println("fun2");
System.out.println("---");
}
public static void main(String[] args) {
EnclosingJoinPointEntity entity = new EnclosingJoinPointEntity();
entity.fun1();
}
}
public aspect EnclosingJoinPointEntityAspect {
pointcut enclosePoint(): call(* article.service.EnclosingJoinPointEntity.fun2(..));
before(): enclosePoint() {
System.out.println("thisJoinPoint");
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
System.out.println("--------------");
System.out.println("thisEnclosingJoinPointStaticPart");
System.out.println(thisEnclosingJoinPointStaticPart);
System.out.println(thisEnclosingJoinPointStaticPart.getSourceLocation());
System.out.println();
}
}