Spring AOP学习之aspectj
概念
-
join point
-
pointcut
- advice
- 编译器,织入 (weaving)
Join Point
函数执行时的切入点,类别挺多的
方法:
以Account类的debit方法为例:
execution,如下图
call,如下图
构造函数:
访问属性:
异常处理:
类初始化(静态代码块):
还有其他几种暂时忽略
注意以上的具体代码中,表示的都是一个join cut
Point Cut
多个join point的集合。方便对集合中的join point统一处理。为了与join point对应,point cut也有好几种type。另外,为了方便收集join point,point cut一般会用到通配符。
通配符:
- *,表示任意的字符,不包括括号
- ..,表示任意的字符,包括括号
- +,表示子类
通配符举例:
- 包com.yxm下的所有方法,参数不限,返回不限:* com.yxm..*.*(..),..*查找包中(包括所有子包和后代包)中的所有类,.*表示任意方法,..表示参数的数量和类型不限
- 包com.yxm下的所有方法,不包括子包和后代包:* com.yxm.*.*(..)
- 以"com.yxm.Accout"开头的所有类,比如com.yxm.AccountService: com.yxm.Account*
- com.yxm.Account类的所有子类: com.yxm.Account+
point cut type:
- execution,对应的join point是:方法execution,构造函数
- call,对应的join point是:方法调用call
- get/set,对应的join point是:访问属性
- handler,对应的join point是:异常处理
- staticinitialization,对应的join point是:类初始化(静态代码块)
point cut举例
- 执行com.yxm包下的所有方法,参数不限:execution(* com.yxm..*.*(..));
- 调用com.yxm包下的所有方法,参数不限:call(* com.yxm..*.*(..));
- 调用com.yxm包下的所有方法,不包括子包和后代包: call (* com.yxm.*.*(..));
使用父类方法
如果类com.yxm.Parent有一个方法p();同时它的子类com.yxm.child.Child1定义了父类没有的方法c1;子类com.yxm.child.Child2重写了p()方法,里面还调用了super.p()
- execution(* com.yxm.*.*(..))不会收集到Child1.c1()这个join point
- execution(* com.yxm.*.*(..))会收集到Child1.p()这个join point(因为Child1.p()调用的也是父类的方法)
- execution(* com.yxm.*.*(..))会收集到Child2.p()这个join point,也会收集到super.p()
通配符与类型
- com.yxm.Account,具体的类型
- com.yxm.Account*,com.yxm包下Account开头的类,比如com.yxm.AccountService
- com.yxm..*,com.yxm包和子包后代包中的类
- com.yxm.Account+, Account类/接口的子类
执行环境point cut
- this(类型)
- target(类型)
this表示当前join point收集时,“this”所指的对象;
target表示当前join point收集时,正在执行某个对象(target)的方法/变量
以在类Main中调用Account类的debit方法举例:
call (* Account.*(..)) 收集到的join point中,this指向了Main对象,target指向了Account对象
execution (* Account.*(..)) 收集到的join point 中,this和target都是Account对象
使用this和target可以获取join point执行时的执行环境相关的对象,类型不能使用通配符只能使用具体的类型,不过实际获取到的this/target对象可以是此类型的子类
参数point cut: args
- args(String a, ..)收集参数列表第一个参数是String,其他参数的类型/数量不限制
- args(String a, int b), 收集参数列表有2个参数,第一个String类型,第二个int类型的join point
point cut的组合使用
- call (* com.yxm..*.*(..)) && this(com.yxm.Account);
- call (* com.yxm..*.*(..)) && args(String a);
Advice
使用point cut收集到join point之后,用advice对point cut做切面编程,主要的advice type是:
- before
- after
- after returning
- around
- 其他
aspectj编译器,织入
织入(weaving),将AOP代码逻辑与核心代码逻辑融合
织入可以是:
- 编译时织入,需要特殊的编译器
- 编译后织入,需要修改核心代码编译后的二进制文件
- 加载类时织入,需要特殊的classloader
- 运行时织入,可以使用动态代理来实现运行时织入
aspectj支持编译时,编译后,加载类时织入,不支持运行时织入(Spring AOP会用运行时织入)
如果使用编译时织入,需要下载aspectj 自己的编译器acj编译代码
官网下载,使用ajc命令代替javac命令编译,编译得到是普通的.class文件,使用java命令即可运行。
通过反编译.class文件可以看到aspectj使用的是编译时修改.class文件的字节码实现AOP,不基于继承,也不基于代理模式(包括动态代理和静态代理)
下图是反编译的代码中一个before execution添加的