1.什么是AOP
AOP 指的是面向切面编程——针对业务过程中的切面(相同代码)进行提取,面对的是处理过程中的某一步骤或阶段
2.AOP作用
AOP 关注点是共同处理,可以通过配置将其作用到某一个或多个目标对象上。好处是实现组件重复利用,改善程序结构,提高灵活性。将共通组件与目标对象解耦。
AOP主要用在处理共通逻辑,如性能统计、日志记录、事务处理、异常处理等,利用AOP可以将这些共通逻辑从普通的业务逻辑代码中分离处理。如果要修改这些逻辑也不会影响普通业务逻辑代码。
3.基本概念及原理
切面(Aspect):封装共通代码的组件,可以作用到其他目标组件的方法上。
目标(target):一个或多个方面所作用的对象
切入点(Pointcut):用于指定哪些组件哪些方法使用切面组件,Spring提供表达式来实现该指定
方法限定表达式:execution(返回值类型 方法名(参数))
通知(Advice):用于指定组件作用到目标组件的具体位置
4.AOP实现原理:基于动态代理技术
5.AOP开发步骤
xml配置方式:
A.创建切面组件类
B.spring配置文件中申明切面组件
C.spring配置文件中使切面组件作用到目标组件的方法上
1 <aop:config> 2 <!-- aop:pointcut 设置切入点 即哪些类的哪些方法需要使用切面组件切入功能 3 expression:通过设置切入点表达式来控制切入点 4 按类型匹配的切入点表达式 5 within(包名.类名) 6 a.匹配到具体的类 如 within(com.rong.web.service.EmpServiceImpl) 7 --> 8 <aop:pointcut expression="within(com.rong.web.service.EmpServiceImpl)" id="myPointcut"/> 9 <!-- 10 aop:aspect 设置切面功能 ref用来指定哪个切面类 11 --> 12 <aop:aspect id="" ref="myLogger"> 13 <!-- 设置通知类型 --> 14 <!-- aop:before 前置通知,在目标方法执行前来执行切面操作 15 method:指定切面类哪个方法进行切面操作 16 pointcut-ref:设置上面定义好的切入点对象 17 --> 18 <!-- <aop:before method="doBefore" pointcut-ref="myPointcut"/> --> 19 <!--后置通知--> 20 <aop:after method="doAfter" pointcut-ref="myPointcut"/> 21 <!-- 环绕通知 --> 22 <!-- <aop:around method="doAound" pointcut-ref="myPointcut"/> --> 23 <!-- 异常通知 24 throwing:需要指定当前的异常对象 需要与切面类的异常通知方法中的参数名对应 25 --> 26 <aop:after-throwing method="doException" throwing="e" pointcut-ref="myPointcut" /> 27 <aop:after-returning method="doAfterReturning" pointcut-ref="myPointcut"/> 28 </aop:aspect> 29 </aop:config>
通知类型:
前置通知(before):先执行切面功能再执行目标功能
后置通知(after):先执行目标功能再执行切面功能(无异常情况)
最终通知(after-returning):先执行目标功能再执行切面功能(有无异常都执行切面)
异常通知(after-throwing):先执行目标,捕获抛出异常后执行切面
环绕通知(around):先执行切面前置部分,然后执行目标,最后再执行切面后置部分
切入点表达式:
类匹配: 语法within(包名.类名)
匹配到具体的类
方法匹配:
execution(返回值类型 包名.类名.方法名(参数类型,参数类型..))
注解方式实现AOP:
A.xml配置文件中开启组件扫描和注解功能
<context:component-scan base-package="com.rong.web"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
B.目标组件注解
C.切面组件注解
@Component:声明普通、一般组件
@Aspect:声明切面组件
@Before("within(com..UserBizImpl)"):设置前置通知
@AfterReturning("within(com..UserBizImpl)"):设置后置通知
@Around("within(com..UserBizImpl)"):设置环绕通知
@AfterThrowing(pointcut="within(com..UserBizImpl)",throwing="e"):设置异常通知
@After("within(com..UserBizImpl)"):设置最终通知
1 /** 2 * 切面组件类 定义共通的业务逻辑 3 * 4 * @Aspect: 声明当前bean为切面组件bean 5 * @Before(切入点表达式):声明当前方法为前置通知 6 * 7 * 8 * within:按类型匹配 9 * within(包名.类名):给某个具体类的所有方法进行aop切面编程 10 * within(包名.*): 给当前包下(不包括子包)下的所有类切面编程 11 * within(包名..*):给当前包下(包括子包)下的所有类切面编程 12 * 13 * execution:按方法匹配 14 * execution(返回值类型 包名.类名.方法名(参数类型,参数类型,...)) 15 * execution(* com.rong.web.service..*.find*(..)): 16 * 第一个*是表示任意的方法返回值类型 17 * service..*表示service包和子包下的任意类 18 * find*:表示任意以find开头的方法 19 * (..)表示方法中的参数任意 20 * 21 * @author 容杰龙 22 */ 23 //@Aspect 24 //@Component//声明组件bean注解
1 public class MyLogger { 2 3 //定义前置通知 在方法执行前执行 4 //@Before("within(com.rong.web.service.*)") 5 public void doBefore(){ 6 System.out.println("日志记录前置操作。。。。。。"); 7 } 8 9 10 @After("within(com..*)") 11 //后置通知 12 public void doAfter(){ 13 System.out.println("日志记录后置通知操作。。。。"); 14 } 15 16 /** 17 * 18 * 给程序中的所有的find开头的方法添加环绕通知 19 * 20 * ProceedingJoinPoint : 封装了目标组件信息的类 21 * @param joinPoint 22 */ 23 24 // @Around("execution(* com.rong.web.service..*.find*(..))") 25 public void doAound(ProceedingJoinPoint joinPoint){ 26 //getSignature()获取目标方法 27 String methodName = joinPoint.getSignature().getName(); 28 String className = joinPoint.getTarget().getClass().getName();//获取目标组件类名 29 30 //环绕通知前置功能 31 System.out.println(className+"的"+methodName+"开始执行......"); 32 long start = System.currentTimeMillis(); 33 34 try { 35 //执行目标组件的方法 36 Object proceed = joinPoint.proceed();// 等同于empService.findAll() 37 } catch (Throwable e) { 38 e.printStackTrace(); 39 } 40 41 //环绕通知后置功能 42 System.out.println(className+"的"+methodName+"执行完成......"); 43 long end = System.currentTimeMillis(); 44 System.out.println("当前方法总执行时间"+(end-start)); 45 } 46 47 /** 48 * service和子包所有类中的方法生效 49 * 异常通知调用的切面方法 50 * 当发生当前参数类型指定的异常的时候会引发调用,需要注意比当前参数类型大的异常无法引发调用 51 * 52 * pointcut:切入点表达式 53 * throwing:对应方法中的参数 54 */ 55 @AfterThrowing(pointcut="execution(* com.rong.web.service..*.*(..))",throwing="e") 56 public void doException(Exception e){ 57 System.out.println("异常通知调用。。。。。。。。"); 58 e.printStackTrace(); 59 } 60 61 /** 62 * service包下包括子包的所有方法都执行 63 * 返回通知 在执行完目标方法返回之前执行 64 */ 65 @AfterReturning("execution(* com.rong.web.service..*.*(..))") 66 public void doAfterReturning(){ 67 System.out.println("返回通知执行......"); 68 } 69 }