把多个业务流程的公共部分抽取成一个独立的切面,进行统一管理,在合适的时机横向的把切面切入到业务流程的指定位置当中。
举例说明
在一些项目当中,有很多的业务流程都要用户登录之后才能使用。
使用OOP想要解决就是把登录验证模块插入到每一个业务流程当中,并且每一个业务流程需要的登录验证还可能是不统一的。
使用AOP只需要把登录验证模块单独抽取出来统一管理,在需要用到的位置声明需要用到登录验证模块,把登录验证的切面插入到这一切点当中。
Spring本身也有自己的AOP,但是普遍都在使用AspectJ,反正基本上都在用。
导入Spring-AOP及AspectJ依赖jar包
略
xml配置文件当中引入aop命名空间
在ApplicationContext.xml
当中加入
自动代理
每一个切面都是一个IoC容器当中的bean, 都需要用``@Component`等注解标注装配到容器当中
声明切面
@Aspect
@Component
public class TestAspect{
//
}
上文当中已经提到了通知的含义,需要知道所有的通知都是在切面对象当中的方法
在除环绕通知外的所有通知当中都可以指定一个JoinPoint对象用来获得目标方法的信息(非必须),而环绕通知必须包含ProceedingJoinPoint参数,如果不包含这个参数将无法调用目标方法,Object为返回值
前置通知使用``@Before注解,
@Before中的参数为切点表达式,**可以使用
*`占位符**
@Before("execution(* com.nond.demo01.Calc.*(int,int))")
public void beforeAdvice(JoinPoint point) {
System.out.println("Method:"+point.getSignature().getName()+" begin,With:"+Arrays.asList(point.getArgs()));
}
需要注意后置通知是无论代码是否抛出异常都会执行后置通知的代码,也即后置通知是在连接点完成的会后执行的,但是在我实际的测试当中,后置通知的打印语句是先于返回通知和异常通知执行的
前置通知可以获取到方法的入参值,但是后置通知不能获取到该方法执行完后的结果,需要在返回通知里面去获取结果值
@After("execution(* com.nond.demo01.Calc.*(int,int))")
public void afterAdvice(JoinPoint point) {
System.out.println("Method:"+point.getSignature().getName()+" end,With:"+Arrays.asList(point.getArgs()));
}
在返回通知中,注解需要指定两个参数
@AfterReturning(value="execution(* com.nond.demo01.Calc.*(int,int))",returning="result")
public void returningAdvice(JoinPoint point,Object result) {
System.out.println("Method:"+point.getSignature().getName()+" returning,With:"+Arrays.asList(point.getArgs())+",Result="+result);
}
在异常通知中,注解需要指定两个参数
@AfterThrowing(value="execution(* com.nond.demo01.Calc.*(int,int))",throwing="e")
public void throwingAdvice(JoinPoint point,Throwable e) {
System.out.println("Method:"+point.getSignature().getName()+" returning,With:"+Arrays.asList(point.getArgs())+",Throwing="+t);
}
@Around
注解标注,注解内的参数为切点表达式Throwable
,即point.proceed()
抛出java.lang.Throwable
@Around("execution(...)")
public Object arroundAdvice(ProceedingJoinPoint point)throws Throwable{
Object result = null;
try{
System.out.println("前置通知");
result = point.proceed();
System.out.println("返回通知,返回值:"+result);
}catch(Throwable e){
System.out.println("异常通知,异常:"+e);
throw e;
}
System.out.println("后置通知");
return result;
}
当有多个切面同时作用于同一个连接点时,如果没有指定切面的优先级会出现优先级混乱的情况,即哪一个切面优先是不确定的
可以使用两种方法给切面指定优先级
getOrder()
方法返回值越小则切片的优先级越高@Ordered
,指定优先级,参数越小则切片优先级越高 在一个切面当中多个通知共同使用一个切点时,可以吧切点表达式单独抽取出来复用
@Aspect
@Component
public class TestAspect{
@PointCut("execution(* com.nond.demo01.Calc.*(int,int)))
public publicPointCut(){
}
@Before("publicPointCut()")
public beforeAdvice(JoinPoint point){
...
}
@After("publicPointCut()")
public afterAdvice(JoinPoint point){
...
}
}
使用XML配置AOP的思想与使用注解配合基本相同
使用注解方式需要配置的内容在xml方式都要配置
bean>
<bean name="calc" class="com.nond.demo01.Calc">bean>
在
内配置切点、连接点和通知
<aop:config>
<aop:pointcut expression="execution(* com.nond.demo01.Calc.*(int,int))" id="pointcut">aop:pointcut>
<aop:aspect ref="loggerAspect" order="1">
<aop:before method="beforeAdvice" pointcut-ref="pointcut"/>
<aop:after method="afterAdvice" pointcut-ref="pointcut"/>
<aop:after-returning method="returningAdvice" returning="result" pointcut-ref="pointcut"/>
<aop:after-throwing method="throwingAdvice" throwing="t" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>