基于Spring AOP @AspectJ进阶说明
@aspectj可以使用切点函数定义切点,我们还可以使用逻辑运算符对切点进行复核运算得到复合的切点,为了在切面中重用切点,我们还可以对切点进行命名,以便在其他的地方引用定义过的切点。
当一个连接点匹配多个切点时,需要考虑织入顺序的问题,此外一个重要的问题是如何再增强中访问连接点上下文的信息。
waiter接口:
package com.yyq.aspectjadvanced; public interface waiter { void greetto(string name); void serveto(string name); }
naivewaiter实现类:
package com.yyq.aspectjadvanced; public class naivewaiter implements waiter { @override public void greetto(string name) { system.out.println("naivewaiter:greet to " + name + "..."); } @override public void serveto(string name) { system.out.println("naivewaiter:serving to " + name + "..."); } public void smile(string clientname,int times){ system.out.println("naivewaiter:smile to "+clientname+ times+"times..."); } }
naughtywaiter实现类:
package com.yyq.aspectjadvanced; public class naughtywaiter implements waiter { public void greetto(string clientname) { system.out.println("naughtywaiter:greet to " + clientname + "..."); } public void serveto(string clientname) { system.out.println("naughtywaiter:serving " + clientname + "..."); } public void joke(string clientname, int times) { system.out.println("naughtywaiter:play " + times + " jokes to " + clientname + "..."); } }
seller接口:
package com.yyq.aspectjadvanced; public interface seller { int sell(string goods, string clientname); }
smallseller实现类:
package com.yyq.aspectjadvanced; public class smartseller implements seller { public int sell(string goods,string clientname) { system.out.println("smartseller: sell "+goods +" to "+clientname+"..."); return 100; } public void checkbill(int billid){ if(billid == 1) throw new illegalargumentexception("iae exception"); else throw new runtimeexception("re exception"); } }
beans.xml配置文件:
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="naivewaiter" class="com.yyq.aspectjadvanced.naivewaiter"/> <bean id="naughtywaiter" class="com.yyq.aspectjadvanced.naughtywaiter"/> <bean id="seller" class="com.yyq.aspectjadvanced.smartseller"/> <!-- <bean class="com.yyq.aspectjadvanced.testaspect"/> <bean class="com.yyq.aspectjadvanced.testaspect2"/> <bean class="com.yyq.aspectjadvanced.testaspect3"/> <bean class="com.yyq.aspectjadvanced.testaspect4"/> <bean class="com.yyq.aspectjadvanced.testaspect5"/> <bean id="naivewaiter2" class="com.yyq.aspectjadvanced.naivewaiter2"/> <bean class="com.yyq.aspectjadvanced.testaspect6"/> <bean class="com.yyq.aspectjadvanced.testaspect7"/> <bean class="com.yyq.aspectjadvanced.testaspect8"/> --> </beans>
1、切点符合运算
使用切点符合运算符,我们将拥有强大而灵活的切点表达能力。
testaspect:切点符合运算定义切面
package com.yyq.aspectjadvanced; import org.aspectj.lang.annotation.after; import org.aspectj.lang.annotation.afterreturning; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; @aspect public class testaspect { //与非运算 @before("!target(com.yyq.aspectjadvanced.naivewaiter) && execution(* serveto(..))") public void notserveinnaivewaiter(){ system.out.println("--notserveinnaivewaiter() executed!--"); } //与运算 @after("within(com.yyq.aspectjadvanced.*) && execution(* greetto(..))") public void greettofun(){ system.out.println("--greettofun() executed!--"); } //或运算 @afterreturning("target(com.yyq.aspectjadvanced.waiter) || target(com.yyq.aspectjadvanced.seller)") public void waiterorseller(){ system.out.println("--waiterorseller() executed!--"); } }
测试方法:
@test public void pointaspectjtest() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); waiter naivewaiter = (waiter) ctx.getbean("naivewaiter"); waiter naughtywaiter = (waiter) ctx.getbean("naughtywaiter"); naivewaiter.greetto("john"); naivewaiter.serveto("john"); naughtywaiter.greetto("tom"); naughtywaiter.serveto("tom"); }
输出结果:
naivewaiter:greet to john... --greettofun() executed!-- --waiterorseller() executed!-- naivewaiter:serving to john... --waiterorseller() executed!-- naughtywaiter:greet to tom... --greettofun() executed!-- --waiterorseller() executed!-- --notserveinnaivewaiter() executed!-- naughtywaiter:serving tom... --waiterorseller() executed!--
2、命名切点
切点直接声明在增强方法处被称为匿名切点,匿名切点只能在声明处使用。如果希望在其他地方重用一个切点,我们可以通过@pointcut注解以及切面类方法对切点进行命名。
testnamepointcut:命名切点类
package com.yyq.aspectjadvanced; import org.aspectj.lang.annotation.pointcut; public class testnamepointcut { //通过注解方法inpackage()对该切点进行命名,方法可视域修饰符为private,表明该命名切点只能在本切面类中使用 @pointcut("within(com.yyq.aspectjadvaned.*)") private void inpackage(){} @pointcut("execution(* greetto(..))") protected void greetto(){} @pointcut("inpackage() and greetto()") public void inpkggreetto(){} }
testaspect2:切面实现类
package com.yyq.aspectjadvanced; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; @aspect public class testaspect2 { @before("testnamepointcut.inpkggreetto()") public void pkggreetto(){ system.out.println("--pkggreetto() executed!--"); } @before("target(com.yyq.aspectjadvanced.naivewaiter) || testnamepointcut.inpkggreetto()") public void pkggreettonotnaivewaiter(){ system.out.println("--pkggreettonotnaivewaiter() executed!--"); } }
测试方法:
@test public void pointaspectjtest2() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); naivewaiter naivewaiter = (naivewaiter) ctx.getbean("naivewaiter"); naivewaiter.smile("andy", 2); }
输出结果:
--pkggreettonotnaivewaiter() executed!-- naivewaiter:smile to andy2times...
3、增强织入的顺序
一个连接点可以同时匹配多个切点,切点对应的增强在连接点上的织入顺序的安排主要有以下3种情况:
1)如果增强在同一个切面类中声明,则依照增强在切面类中定义的顺序进行织入;
2)如何增强位于不同的切面类中,且这些切面类都实现了org.springframework.core.order接口,则由接口方法的顺序号决定(顺序号小的先织入);
3)如果增强位于不同的切面类中,且这些切面类没有实现org.springframework.core.order接口,织入的顺序是不确定的。
4、访问连接点信息
aspectj使用org.aspectj.lang.joinpoint接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.proceedingjoinpoint表示连接点对象,该类是joinpoint的子接口,任何一个增强方法都可以通过将第一个入参声明为joinpoint访问到连接点上下文的信息。
testaspect3:切面实现类
@aspect public class testaspect3 { @around("execution(* greetto(..)) && target(com.yyq.aspectjadvanced.naivewaiter)") public void joinpointaccess(proceedingjoinpoint pjp) throws throwable { system.out.println("---joinpointaccess---"); system.out.println("args[0]:" + pjp.getargs()[0]); system.out.println("signature:" + pjp.gettarget().getclass()); pjp.proceed(); system.out.println("---joinpointaccess---"); } }
测试方法:
@test public void pointaspectjtest3() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); waiter naivewaiter = (waiter) ctx.getbean("naivewaiter"); naivewaiter.greetto("andy"); }
输出结果:
---joinpointaccess--- args[0]:andy signature:class com.yyq.aspectjadvanced.naivewaiter naivewaiter:greet to andy... ---joinpointaccess---
5、绑定连接点方法入参
args()用于绑定连接点方法的入参;@annotation()用于绑定连接点方法的注解对象;而@args()用于绑定连接点方法入参的注解。
testaspect4:切面实现类
@aspect public class testaspect4 { @before("target(com.yyq.aspectjadvanced.naivewaiter) && args(name,num,..)") public void bindjoinpointparams(int num, string name) { system.out.println("---bindjoinpointparams---"); system.out.println("name:" + name); system.out.println("num:" + num); system.out.println("---bindjoinpointparams---"); } }
测试方法:
@test public void pointaspectjtest4() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); naivewaiter naivewaiter = (naivewaiter) ctx.getbean("naivewaiter"); naivewaiter.smile("andy", 3); }
输出结果:
---bindjoinpointparams--- name:andy num:3 ---bindjoinpointparams--- naivewaiter:smile to andy 3 times...
6、绑定代理对象
使用this()或target()可绑定被代理对象实例,在通过类实例名绑定对象时,还依然具有原来连接点匹配的功能,只不过类名是通过增强方法中同名入参的类型间接决定罢了。
testaspect5:切面实现类
@aspect public class testaspect5 { @before("this(waiter)") public void bindproxyobj(waiter waiter){ system.out.println("---bindproxyobj---"); system.out.println(waiter.getclass().getname()); system.out.println("---bindproxyobj---"); } }
测试方法:
@test public void pointaspectjtest5() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); waiter waiter = (waiter) ctx.getbean("naivewaiter"); waiter.greetto("yang"); }
输出结果:
---bindproxyobj--- com.yyq.aspectjadvanced.naivewaiter$$enhancerbycglib$$fefafe52 ---bindproxyobj--- naivewaiter:greet to yang...
7、绑定类注解对象
@within()和@target()函数可以将目标类的注解对象绑定到增强方法中,我们通过@within()演示注解绑定的操作。
testaspect6:切面测试类
@aspect public class testaspect6 { @before("@within(m)") public void bindtypeannoobject(monitorable m) { system.out.println("---bindtypeannoobject---"); system.out.println(m.getclass().getname()); system.out.println("---bindtypeannoobject---"); } }
测试方法:
@test public void pointaspectjtest6() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); waiter waiter = (waiter) ctx.getbean("naivewaiter2"); ((naivewaiter2)waiter).greetto("yang"); }
输出结果:
---bindtypeannoobject--- $proxy4 ---bindtypeannoobject--- naivewaiter:greet to yang...
8、绑定返回值
在后置增强中,我们可以通过returning绑定连接点方法的返回值。
testaspect7:切面实现类
@aspect public class testaspect7 { @afterreturning(value = "target(com.yyq.aspectjadvanced.smartseller)", returning = "retval") public void bindreturnvalue(int retval) { system.out.println("---bindreturnvalue---"); system.out.println("returnvalue:" + retval); system.out.println("---bindreturnvalue---"); } }
测试方法:
@test public void pointaspectjtest7() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); smartseller seller = (smartseller) ctx.getbean("seller"); seller.sell("beer", "john"); }
输出结果:
smartseller: sell beer to john... ---bindreturnvalue--- returnvalue:100 ---bindreturnvalue---
9、绑定抛出的异常
和通过切点函数绑定连接点信息不同,连接点抛出的异常必须使用afterthrowing注解的throwing成员进行绑定。
testaspect8:切面实现类
@aspect public class testaspect8 { @afterthrowing(value = "target(com.yyq.aspectjadvanced.smartseller)", throwing = "iae") public void bindexception(illegalargumentexception iae) { system.out.println("---bindexception---"); system.out.println("exception:" + iae.getmessage()); system.out.println("---bindexception---"); } }
测试方法:
@test public void pointaspectjtest8() { string configpath = "com\\yyq\\aspectjadvanced\\beans.xml"; applicationcontext ctx = new classpathxmlapplicationcontext(configpath); smartseller seller = (smartseller) ctx.getbean("seller"); seller.checkbill(1); }
输出结果:
---bindexception--- exception:iae exception ---bindexception---
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。
推荐阅读
-
详解在Spring中如何使用AspectJ来实现AOP
-
spring-aop-aspectj-case 博客分类: spring springaopaspectj
-
Spring实现AOP的4种方式 博客分类: spring spring代理的AOP@AspectJ注解POJOAspectJ
-
基于spring的aop实现多数据源动态切换 博客分类: javaspring 动态切换springaopAbstractRoutingDataSource
-
Spring基于注解的AOP配置
-
详解在Spring中如何使用AspectJ来实现AOP
-
Spring Aop之AspectJ注解配置实现日志管理的方法
-
基于spring中的aop简单实例讲解
-
Spring Aop之AspectJ注解配置实现日志管理的方法
-
Spring AOP 基于注解详解及实例代码