Spring AOP With AspectJ
一.AOP和拦截器
某些情况下,AOP和拦截器包括Filter能够实现同样的功能,一般都是请求即controller层的操作,这三个执行顺序为Filter>Interceptor>AOP,当然这里面的区别我会重新写一篇文章讲解,这里面提一下就是想告诉大家,不一定要使用AOP,个人感觉用Filter和Interceptor实现的更方便简单一点。
二.AOP 准备
在spring框架下,你还需要添加aspectjrt,aspectjweaver和cglib 的相关jar包,maven项目的pom.xml中可直接在maven公服获取配置。
在spring-mvc.xml中配置aop,并添加<aop:aspectj-autoproxy /> 。
对于aspect类,对类添加@Aspect和@Component即可。
三.Spring AOP简介
AOP这个概念看文档比较抽象,网上的举例子也基本上是操作日志这些,不过日志记录的确是一个非常形象的例子。说白了,当我们想对一些批量的方法进行一些补充操作,在service层和dao层,我们一般选择AOP。
日志的例子,当我们dao层我们执行crud的时候同时想在数据库中生成一条日志记录,这时候日志的处理代码其实是一样的,我们可以提取出来。
再比如,某些操作需要发短息通知用户(更改密码,账单到期等等),发短信的代码其实是一样的,就是一个手机号和短信内容。这时候我们用AOP就能解决,所有需要发短信的方法都可以用AOP来解决。好处是:只需要 一个地方维护代码,动态的决定哪些地方给用户发短信。
一般用AOP的时候我们都是配合一些注解来完成的,注解的主要作用有两个方面:1.可以用来判断是否拦截该方法,2.传递一些aop方法中需要的参数或者变量值。
Glossory of AOP:
1.Pointcut: aop拦截的是哪些方法,必须要告诉aop方法,一般我们都是通过类似正则的表示,pointcut有不同的几种类型,通常我们见到的应该就是execution,其实目前我用的也就是execution,其他类型比如:within,this,args,target,@args,@target,@annotation...
下面是pointcut的几个例子: pointcut的表达式可以通过'||','&&'和'!'连接
@Pointcut("execution(pubic * *(..))")
private void allOperation() {}
@Pointcut("within(com.cnblog.service.User..") =>@Pointcut("execution(* com.cnblog.service.User.*(..))")
private void userOperation() {}
@Pointcut("allOperation() && userOperation() ")
private void unionOperation() {}
2.Advice: Advice is associated with a pointcut expression, and runs before, after, or around method executions matched by the pointcut. The pointcut expression may be either a simple reference to a named pointcut, or a pointcut expression declared in place.
我们通常最简洁的用法就是@Around("execution(public *.update*(..))"),其实它等同于@Around(pointcut="execution(public *.update*(..))"),pointcut的值可以为上面我们定义的pointcut表达式的函数。下面主要介绍四种advice:
Before:在拦截函数执行之前执行,一般数据权限或者用户验证的信息都是这样,类似Interceptor的prehandle。
@Before("execution(public * * (..))")
public void doCheck() {...}
Around:around 是最通常用的,,因为可以before和after都融合,而且可以决定被拦截的方法什么时候执行,advice method的第一个参数必须是ProceedingJoinPoint.通过调用ProceedingJoinPoint的proceed方法就可以执行被拦截的函数。
@Around("execution(public * * (..))")
public Object doAround(ProceedingJoinPoint pjp ,..) {
Object[] args = pjp.getArgs();
...
return pjp.proceed(args);
}
After:在函数执行完之后执行,执行完包含被拦截函数返回值或者出现异常,一般情况我们是针对出现异常做一个记录等。
@After("execution(public * * (..))")
public void doFinal() {...}
注:advice ordering:如果多个advices在同一个连接点执行,这时候就有一个执行顺序,一般情况下我们可以尽量避免,如果无法避免可以通过@Order(org.springframework.core.annotation.Order)来表示先后顺序。优先级越高,在"on the way in"先执行,"on the way out"后执行.
4.Other keys: Introductions,perthis,PessimisticLockingFailureException(详情参照spring官方文档)
四.AOP的选择
Spring AOP or AspectJ ,Aspect language style ,@AspectJ or Spring xml style(aspect schema_based 这里面就不讲了,就是aop的xml配置)。
1.Spring AOP or full AspectJ
spring aop 更简单,因为aspectj还需要引入compiler/weaver相关的jar 包。
如果你的aop切入的是spring beans,可以直接使用spring aop就可以了,如果你想拦截的对象不是spring容器管理的只能使用AspectJ,个人一直使用Aspectj,因 为比较方便,如果用maven的话 就更简单了。
2.个人建议直接使用@Aspect语法风格,使用spring xml配置很繁琐,而且容易遗漏。
五.AOP代理
spring AOP使用JDK动态代理或者cglib来创建目标对象的代理,JDK动态代理会优先选择。
默认情况下:如果被代理的目标对象至少实现了一个接口,那么这时候默认使用JDK动态代理,反之,则使用cglib。
当然,你也可以强制使用cglib,如果你很偏爱它,不过在spring3.x你首先得提供cglib的相关jar包。然后需要在spring 中配置<aop:config proxy-target-class="true" />,to force CGLIB proxying when using @AspectJ autoproxy support ==> <aop:aspectj-autoproxy proxy-target-class="true" />
理解AOP代理:
一般object 调用:interface pojo ,class SimplePojo implements pojo
public classMain { public static voidmain(String[] args) { Pojo pojo = new SimplePojo(); // this is a direct method call on the 'pojo' reference pojo.foo(); } }
我们稍微改变一下,当客户端只有一个代理的时候 ,如图:
public classMain { public static voidmain(String[] args) { ProxyFactory factory = newProxyFactory(newSimplePojo()); factory.addInterface(Pojo.class); factory.addAdvice(newRetryAdvice()); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); } }