AOP面向切面编程

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 }
原文地址:https://www.cnblogs.com/57rongjielong/p/7801001.html