在软件开发中,分布于应用多出的功能被称为和横切关注点. 通常,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往直接嵌入到应用的业务逻辑中).将这些横切关注点与业务逻辑相分离正是面向切面编成(AOP)所要解决的
要了解什么是AOP,首先应该了解AOP相关术语,只有完全理解这些,才能更好的掌握AOP
描述切面的常用术语有 通知(advice), 切点(pointcut), 连接点(join point).
通知(advice)
切面有目标-他必须要完成的工作,成为通知.通知定义了切面是什么以及何时使用.
Spring切面可以应用5种类型的通知
- Before 在方法被调用之前调用通知
- After 在方法完成之后调用通知,无论方法执行是否成功
- After-returning 在方法成功执行之后调用通知
- After-throwing 在方法抛出异常后调用通知
- Around 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
连接点(join point)
连接点是在应用执行过程中能够插入切面的一个点.这个点可以是调用方法时,抛出异常时,甚至修改一个字段时.切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为
切点(pointcut)
切点的定义会匹配通知所要织入的一个或多个连接点
切面(Aspect)
切面是通知和切点的结合,通知和切点共同定义了关于切面的全部内容-它是什么,在何时和何处完成其功能
Spring提供了4种各具特色的AOP支持
- 基于代理的经典AOP
- @AspectJ注解驱动的切面
- 纯POJO切面
- 注入式AspectJ切面(适合Spring各个版本)
以下介绍如何使用注解,在Spring使用AOP
1. XML方式配置
在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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <context:component-scan base-package="com.spring.aop"></context:component-scan> <bean id="studentService" class="com.spring.aop.service.impl.StudentServiceImpl" ></bean> <bean id="studentServiceAspect" class="com.spring.aop.advice.StudentServiceAspect"></bean> <aop:config> <aop:aspect id="studentServiceAspect" ref="studentServiceAspect"> <aop:pointcut expression="execution(* com.spring.aop.service.*.*(..))" id="business"/> <aop:before method="doBefore" pointcut-ref="business"/> <aop:after method="doAfter" pointcut-ref="business" /> <aop:around method="doAround" pointcut-ref="business"/> <aop:after-returning method="doAfterReturning" pointcut-ref="business"/> <aop:after-throwing method="doAfterThrow" pointcut-ref="business" throwing="ex"/> </aop:aspect> </aop:config> </beans>
aop类:
public class StudentServiceAspect { //前置通知 public void doBefore(JoinPoint jp){ System.out.println("doBefore beginning..."); System.out.println("类名:"+jp.getTarget().getClass()); System.out.println("方法名:"+jp.getSignature().getName()); System.out.println(Arrays.asList(jp.getArgs())); System.out.println("doBefore end."); } //后置通知 public void doAfter(JoinPoint jp){ System.out.println("doAfter beginning..."); System.out.println("类名:"+jp.getTarget().getClass()); System.out.println("方法名:"+jp.getSignature().getName()); System.out.println("参数:"+Arrays.asList(jp.getArgs())); System.out.println("doAfter end."); } //环绕通知相当于doBefore 和 doAfter的结合 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("doAround beginning..."); Object retVal = pjp.proceed(); System.out.println(retVal); System.out.println("doAfter Proceed..."); return retVal; } public void doAfterReturning(JoinPoint jp){ System.out.println("返回通知"); } public void doAfterThrow(JoinPoint jp,Throwable ex){ System.out.println("出错异常:"+ ex.getMessage()); } }
2. 注解的方式
xml中加入:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
aop类加上注解:
import java.util.Arrays; import org.aspectj.lang.JoinPoint; 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; import org.springframework.stereotype.Component; @Component("studentServiceAspect") @Aspect public class StudentServiceAspect { @Pointcut("execution(* com.spring.aop.service.*.*(..))") public void pointcut(){} @Before(value = "pointcut()") //前置通知 public void doBefore(JoinPoint jp){ System.out.println("doBefore beginning..."); System.out.println("类名:"+jp.getTarget().getClass()); System.out.println("方法名:"+jp.getSignature().getName()); System.out.println(Arrays.asList(jp.getArgs())); System.out.println("doBefore end."); } @After("pointcut()") //后置通知 public void doAfter(JoinPoint jp){ System.out.println("doAfter beginning..."); System.out.println("类名:"+jp.getTarget().getClass()); System.out.println("方法名:"+jp.getSignature().getName()); System.out.println("参数:"+Arrays.asList(jp.getArgs())); System.out.println("doAfter end."); } @Around("pointcut()") //环绕通知相当于doBefore 和 doAfter的结合 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("doAround beginning..."); Object retVal = pjp.proceed(); System.out.println(retVal); System.out.println("doAfter Proceed..."); return retVal; } @AfterReturning("pointcut()") public void doAfterReturning(JoinPoint jp){ System.out.println("返回通知"); } @AfterThrowing(value="pointcut()",throwing="ex") public void doAfterThrow(JoinPoint jp,Throwable ex){ System.out.println("出错异常:"+ ex.getMessage()); } }
就是这么简单,一个简单的POJO加上一个@Aspect ,定义一个或者多个切点(pointcut).N个通知方法(advice).
切点配合通知便是切面,切面便是AOP(在什么位置,什么时候,做什么)