1、Spring除了支持Schema方式配置AOP,还支持注解方式:使用@Aspect来配置
2、Spring默认不支持@Aspect风格的切面声明,通过如下配置开启@Aspect支持:
- <aop:aspectj-autoproxy/>
3、通过以上配置,Spring就能发现用@Aspect注解的切面内并把它应用到目标对象上。
4、定义一个切面:
- @Aspect
- public class AspectStyle {
- @Before("execution(* com.sxit..*.*(..))")
- public void before(){
- System.out.println("方法执行前执行.....");
- }
- }
5、后置返回通知:
- @AfterReturning("execution(* com.sxit..*.*(..))")
- public void afterReturning(){
- System.out.println("方法执行完执行.....");
- }
6、后置异常通知:
- @AfterThrowing("execution(* com.sxit..*.*(..))")
- public void throwss(){
- System.out.println("方法异常时执行.....");
- }
7、后置最终通知:
- @After("execution(* com.sxit..*.*(..))")
- public void after(){
- System.out.println("方法最后执行.....");
- }
8、环绕通知:
- @Around("execution(* com.sxit..*.*(..))")
- public Object around(ProceedingJoinPoint pjp){
- System.out.println("方法环绕start.....");
- try {
- pjp.proceed();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- System.out.println("方法环绕end.....");
- }
9、按上面的每一个通知都要写一个定义,其实这部分可以抽出来,定义个一个公共的切入点。
- package com.sxit;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.AfterThrowing;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.annotation.Pointcut;
- @Aspect
- public class AspectStyle {
- @Pointcut("execution(* com.sxit..*.*(..))")
- public void init(){
- }
- @Before(value="init()")
- public void before(){
- System.out.println("方法执行前执行.....");
- }
- @AfterReturning(value="init()")
- public void afterReturning(){
- System.out.println("方法执行完执行.....");
- }
- @AfterThrowing(value="init()")
- public void throwss(){
- System.out.println("方法异常时执行.....");
- }
- @After(value="init()")
- public void after(){
- System.out.println("方法最后执行.....");
- }
- @Around(value="init()")
- public Object around(ProceedingJoinPoint pjp){
- System.out.println("方法环绕start.....");
- Object o = null;
- try {
- o = pjp.proceed();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- System.out.println("方法环绕end.....");
- return o;
- }
- }
10、打印信息:
- 方法before前执行.....
- 方法环绕start.....
- 我看.....................
- 方法after执行.....
- 方法环绕end.....
- 方法afterReurning执行.....
Aspectj切入点语法定义
在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"
例如定义切入点表达式
execution()是最常用的切点函数,其语法如下所示:
AspectJ的Execution表达式
execution()
execution()是最常用的切点函数,其语法如下所示:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
1)通过方法签名定义切点
匹配所有目标类的public方法,但不匹配SmartSeller和protected voidshowGoods()方法。第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;
匹配目标类所有以To为后缀的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一个*代表返回类型,而*To代表任意以To为后缀的方法;
2)通过类定义切点
匹配Waiter接口的所有方法,它匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。第一个*代表返回任意类型,com.baobaotao.Waiter.*代表Waiter接口中的所有方法;
匹配Waiter接口及其所有实现类的方法,它不但匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()这两个Waiter接口定义的方法,同时还匹配NaiveWaiter#smile()和NaughtyWaiter#joke()这两个不在Waiter接口中定义的方法。
3)通过类包定义切点
在类名模式串中,“.*”表示包下的所有类,而“..*”表示包、子孙包下的所有类。
匹配com.baobaotao包下所有类的所有方法;
匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类;
匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点。
4)通过方法入参定义切点
切点表达式中方法入参部分比较复杂,可以使用“*”和“..”通配符,其中“*”表示任意类型的参数,而“..”表示任意类型参数且参数个数不限。
匹 配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int);
匹 配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,doubled2,String s3)则不匹配;
匹配目标类中的joke()方法,该方法第 一个入参为String,后面可以有任意个入参且入参类型不限,如joke(Strings1)、joke(String s1,String s2)和joke(String s1,double d2,Strings3)都匹配。
匹 配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(Strings1)和joke(Client c)。如果我们定义的切点是execution(*joke(Object)),则只匹配joke(Object object)而不匹配joke(Stringcc)或joke(Client c)。
args()和@args()
args()函数的入参是类名,@args()函数的入参必须是注解类的类名。虽然args()允许在类名后使用+通配符后缀,但该通配符在此处没有意义:添加和不添加效果都一样。
1)args()
该函数接受一个类名,表示目标类方法入参对象按类型匹配于指定类时,切点匹配,如下面的例子:
args(com.baobaotao.Waiter)
表 示运行时入参是Waiter类型的方法,它和execution(**(com.baobaotao.Waiter))区别在于后者是针对类方法的签名而言的,而前者则针对运行时的入参类型而言。如args(com.baobaotao.Waiter)既匹配于addWaiter(Waiterwaiter),也匹配于addNaiveWaiter(NaiveWaiter naiveWaiter),而execution(**(com.baobaotao.Waiter))只匹配addWaiter(Waiterwaiter)方法;实际上,args(com.baobaotao.Waiter)等价于execution(**(com.baobaotao.Waiter+)),当然也等价于args(com.baobaotao.Waiter+)。
2)@args()
该函数接受一个注解类的类名,当方法的运行时入参对象标注发指定的注解时,方法匹配切点。