• Spring AOP


      Spring AOP 的实现是基于Java的代理机制,从JDK1.3开始就支持代理功能。

     

    1.AOP的一些概念

      描述AOP常用的一些术语有通知(Adivce)、切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)

    • 通知(Advice)

      通知分为五中类型:

      Before
      在方法被调用之前调用
      After
      在方法完成后调用通知,无论方法是否执行成功
      After-returning
      在方法成功执行之后调用通知
      After-throwing
      在方法抛出异常后调用通知
      Around
      通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为

    • 连接点(Join point)

      A point during the execution of a program, such as the execution of a method or the handling of an exception.
      比如:方法调用、方法执行、字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点

      理论上, 程序执行过程中的任何时点都可以作为作为织入点, 而所有这些执行时点都是 Joint point

      但 Spring AOP 目前仅支持方法执行 (method execution)

    • 切点(Pointcut)

      通知(advice)定义了切面何时,那么切点就是定义切面“何处” 描述某一类 Joint points, 比如定义了很多 Joint point, 对于 Spring AOP 来说就是匹配哪些方法的执行
      描述方式:
      直接指定 Jointpoint 所在的方法名, 功能比较单一, 通常只支持方法级别的 AOP 框架

    • 正则表达式

      特定的描述语言, 如 AspectJ 提供的 Pointcut 描述语言

    • 切面(Aspect)

      切面是切点和通知的结合。通知和切点共同定义了关于切面的全部内容,它是什么时候,在何时和何处完成功能

    • 引入(Introduction)

      引用允许我们向现有的类添加新的方法或者属性

    • 织入(Weaving)

      组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

     

    2.Spring 对AOP的支持

    • AOP框架种类

    AspectJ
    JBoss AOP
    Spring AOP
    Spring提供了四种各具特色的AOP支持
    基于代理的经典AOP
    @AspectJ 注解驱动的切面
    纯POJO切面
    注入式AspectJ切面(适合Spring各个版本)
    前面三中都是Spring基于代理的AOP变体,因此,Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单的方法拦截范畴,那么应该考虑在ASpectJ里实现切面,利用Spring的DI把Spring BEan注入到ASpectJ切面中

    3.Spring实现方式

    实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
    重点关注动态代理技术
    Spring的动态代理包括两个部分:

    • JDK动态代理

    JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

    Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

    • CGLib动态代理

    CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
    Spring AOP 框架对 AOP 代理类的处理原则是:
    如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
    如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心。
    Spring AOP 会动态选择使用 JDK 动态代理、CGLIB 来生成 AOP 代理,如果目标类实现了接口,Spring AOP 则无需 CGLIB 的支持,直接使用 JDK 提供的 Proxy 和 InvocationHandler 来生成 AOP 代理即可。

     

    4.AOP实例

    1)导入aopalliance-1.0.jar、aspectjrt-1.7.4.jar、aspectjweaver-1.7.4.jar3个jar包
    2)配置xml文件
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"  
     3     xmlns:mvc="http://www.springframework.org/schema/mvc"  
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5     xmlns:p="http://www.springframework.org/schema/p"  
     6     xmlns:context="http://www.springframework.org/schema/context"  
     7     xmlns:aop="http://www.springframework.org/schema/aop" 
     8     xmlns:tx="http://www.springframework.org/schema/tx" 
     9     xsi:schemaLocation="   
    10            http://www.springframework.org/schema/beans   
    11            http://www.springframework.org/schema/beans/spring-beans.xsd   
    12            http://www.springframework.org/schema/context   
    13            http://www.springframework.org/schema/context/spring-context.xsd  
    14            http://www.springframework.org/schema/mvc   
    15            http://www.springframework.org/schema/mvc/spring-mvc.xsd
    16            http://www.springframework.org/schema/aop 
    17            http://www.springframework.org/schema/aop/spring-aop.xsd
    18            http://www.springframework.org/schema/tx 
    19            http://www.springframework.org/schema/tx/spring-tx.xsd
    20            ">                 
    21  
    22     <context:component-scan base-package="com.zhwy.misp"/>
    23    
    24     <!--aop-->
    25     <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    26           
    27     <mvc:default-servlet-handler />
      3)编写一个测试切面TestAOP.java
     1 import java.util.Arrays;
     2 import org.aspectj.lang.JoinPoint;
     3 import org.aspectj.lang.ProceedingJoinPoint;
     4 import org.aspectj.lang.annotation.After;
     5 import org.aspectj.lang.annotation.AfterReturning;
     6 import org.aspectj.lang.annotation.AfterThrowing;
     7 import org.aspectj.lang.annotation.Around;
     8 import org.aspectj.lang.annotation.Aspect;
     9 import org.aspectj.lang.annotation.Before;
    10 import org.aspectj.lang.annotation.Pointcut;
    11 import org.springframework.core.annotation.Order;
    12 import org.springframework.stereotype.Component;
    13 
    14 @Component
    15 @Order(1)
    16 @Aspect
    17 public class TestAOP {
    18     
    19     /**
    20      * 切入点
    21      */
    22     @Pointcut("execution(* com.zhwy.misp.action.*.*(..))")
    23     public void pointCut(){}
    24     
    25     /**
    26      * 前置通知
    27      */
    28     @Before(value = "pointCut()")
    29     public void beforeMethod(JoinPoint joinPoint){
    30         String methodName = joinPoint.getSignature().getName();
    31         System.out.println("前置通知执行了,methodName:"+methodName);
    32     }
    33     
    34     /**
    35      * 后置通知
    36      */
    37     @After("pointCut()")
    38     public void afterMethod(JoinPoint joinPoint){
    39         String methodName = joinPoint.getSignature().getName();
    40         System.out.println("后置通知执行了,methodName:" + methodName);
    41     }
    42     
    43     /**
    44      * 返回通知——可以访问到方法的返回值
    45      */
    46     @AfterReturning(value="pointCut()", returning="result")
    47     public void afterReturnMethod(JoinPoint joinPoint, Object result){
    48         String methodName = joinPoint.getSignature().getName();
    49         System.out.println("返回通知执行了,methodName:" + methodName+result);
    50     }
    51     
    52     /**
    53      * 异常通知——方法发生异常执行
    54      * 可以访问到异常对象;且可以指定在出现特定异常时执行的代码
    55      */
    56     @AfterThrowing(value="pointCut()",throwing="ex")
    57     public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
    58         String methodName = joinPoint.getSignature().getName();
    59         System.out.println("异常通知了,methodName:" + methodName + ex);
    60     }
    61     /**
    62      * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数)
    63      * 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法
    64      * 且环绕通知必须有返回值,返回值即目标方法的返回值
    65      */
    66     @Around(value="pointCut()")
    67     public Object aroundMethod(ProceedingJoinPoint point){
    68         Object result = null;
    69         String methodName = point.getSignature().getName();
    70         try {
    71             //前置通知
    72             System.out.println("前置通知:"+ methodName + Arrays.asList(point.getArgs()));
    73             //执行目标方法
    74             result = point.proceed();
    75             //返回通知
    76             System.out.println("返回通知:"+ methodName + result);
    77         } catch (Throwable ex) {
    78             //异常通知
    79             System.out.println("异常通知:"+methodName + ex);
    80             throw new RuntimeException(ex);
    81         }
    82         //后置通知
    83         System.out.println("后置通知:"+ methodName);
    84         return result;
    85     }    
    86 }
    • 切面首先是IOC容器中的一个bean,即在切面类前加上标签@Component标签
    • 添加标签@Aspect标签,声明该类是一个切面
    • 实现需要的通知,在方法之前添加如下的通知类型:

    @Before:前置通知,在方法前通知;
    @After :后置通知,在方法执行后通知;
    @AfterRunning:返回通知,在方法返回结果之后通知;
    @AfterThrowing:异常通知,在方法抛出异常之后通知;
    @Around:环绕通知,围绕着方法执行;

    • 切入点表达式的书写:

    execution(* com.zhwy.misp.action.*.*(..)) :第一个*表示任意的修饰符(public/private/protected)及任意的返回值(void/Object);第二个第三个*表示任意的方法,‘..’表示任意数量的参数;

    execution(public * com.zhwy.misp.action.*data.*(..)):表示com.zhwy.misp.action包下以data结尾的公共的方法(public)的方法;

    execution(public void com.zhwy.misp.action.*data.*(..)):表示com.zhwy.misp.action包下以data结尾的公共的方法(public)返回类型是void的方法;

    execution(public void com.zhwy.misp.action.*data.*(int,..)):表示com.zhwy.misp.action包下以data结尾公共的方法(public)返回类型是void的类第一个参数是int的方法;

    execution(public void com.zhwy.misp.action.*data.*(int,int)):表示com.zhwy.misp.action包下以data结尾公共的方法(public)返回类型是void的类第一个参数是int第二个参数是int的方法;

    可以在方法中声明一个类型为JoinPoint的参数,然后就可以访问链接细节,如方法名称和参数值;

      5. 基于XML配置的AOP

      1)在spring配置文件中添加aop的命名空间,配置如下:
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"  
     3     xmlns:mvc="http://www.springframework.org/schema/mvc"  
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5     xmlns:p="http://www.springframework.org/schema/p"  
     6     xmlns:context="http://www.springframework.org/schema/context"  
     7     xmlns:aop="http://www.springframework.org/schema/aop" 
     8     xmlns:tx="http://www.springframework.org/schema/tx" 
     9     xsi:schemaLocation="   
    10            http://www.springframework.org/schema/beans   
    11            http://www.springframework.org/schema/beans/spring-beans.xsd   
    12            http://www.springframework.org/schema/context   
    13            http://www.springframework.org/schema/context/spring-context.xsd  
    14            http://www.springframework.org/schema/mvc   
    15            http://www.springframework.org/schema/mvc/spring-mvc.xsd
    16            http://www.springframework.org/schema/aop 
    17            http://www.springframework.org/schema/aop/spring-aop.xsd
    18            http://www.springframework.org/schema/tx 
    19            http://www.springframework.org/schema/tx/spring-tx.xsd
    20            ">                 
    21  
    22     <context:component-scan base-package="com.zhwy.misp"/>
    23    
    24     <!--aop-->
    25     <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    26     
    27     <!-- 配置切面Bean -->
    28     <bean id="testAOP" class="com.zhwy.misp.aop.TestAOP"></bean>
    29     
    30     <!-- 配置AOP -->
    31     <aop:config>
    32         <!-- 配置切点表达式  -->
    33         <aop:pointcut id="pointcut" expression="execution(* com.zhwy.misp.action.*.*(..))" />
    34         <!-- 配置切面及配置 -->
    35         <aop:aspect order="1" ref="testAOP">
    36             <!-- 前置通知 -->
    37             <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
    38             <!-- 后置通知 -->
    39             <aop:after method="afterMethod" pointcut-ref="pointcut"/>
    40             <!-- 返回通知 -->
    41             <aop:after-returning method="afterReturnMethod" pointcut-ref="pointcut" returning="result"/>
    42             <!-- 异常通知 -->
    43             <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/>
    44         </aop:aspect>
    45     </aop:config>
    46     
      2)编写一个测试切面TestAOP.java
     1 import java.util.Arrays;
     2 import org.aspectj.lang.JoinPoint;
     3 import org.aspectj.lang.ProceedingJoinPoint;
     4 
     5 public class TestAOP {
     6 
     7     /**
     8      * 前置通知
     9      */
    10     public void beforeMethod(JoinPoint joinPoint){
    11         String methodName = joinPoint.getSignature().getName();
    12         System.out.println("前置通知执行了,methodName:"+methodName);
    13     }
    14     
    15     /**
    16      * 后置通知
    17      */
    18     public void afterMethod(JoinPoint joinPoint){
    19         String methodName = joinPoint.getSignature().getName();
    20         System.out.println("后置通知执行了,methodName:" + methodName);
    21     }
    22     
    23     /**
    24      * 返回通知——可以访问到方法的返回值
    25      */
    26     public void afterReturnMethod(JoinPoint joinPoint, Object result){
    27         String methodName = joinPoint.getSignature().getName();
    28         System.out.println("返回通知执行了,methodName:" + methodName+result);
    29     }
    30     
    31     /**
    32      * 异常通知——方法发生异常执行
    33      * 可以访问到异常对象;且可以指定在出现特定异常时执行的代码
    34      */
    35     public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
    36         String methodName = joinPoint.getSignature().getName();
    37         System.out.println("异常通知了,methodName:" + methodName + ex);
    38     }
    39     /**
    40      * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数)
    41      * 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法
    42      * 且环绕通知必须有返回值,返回值即目标方法的返回值
    43      */
    44     public Object aroundMethod(ProceedingJoinPoint point){
    45         Object result = null;
    46         String methodName = point.getSignature().getName();
    47         try {
    48             //前置通知
    49             System.out.println("前置通知:"+ methodName + Arrays.asList(point.getArgs()));
    50             //执行目标方法
    51             result = point.proceed();
    52             //返回通知
    53             System.out.println("返回通知:"+ methodName + result);
    54         } catch (Throwable ex) {
    55             //异常通知
    56             System.out.println("异常通知:"+methodName + ex);
    57             throw new RuntimeException(ex);
    58         }
    59         //后置通知
    60         System.out.println("后置通知:"+ methodName);
    61         return result;
    62     }
    63 }

    6.AOP的坑

    1)AspectJ报错:error at ::0 can't find referenced pointcut XXX

    如果要使用AspectJ完成注解切面需要注意下面的JDK与AspectJ的匹配:

      JDK1.6 —— aspectJ1.6

      JDK1.7 —— aspectJ1.7.3+

      JDK1.8 —— aspectJ1.8+

    2)错误: warning no match for this type name: com.zhwy.misp.service [Xlint:invalidAbsoluteTypeName]

    Spring 切入点配置错误引起的

    expression="execution(* com.zhwy.misp.service.*.*(..))";  后面两个*,表示service包下的所有类下的所有方法

    expression="execution(* com.zhwy.misp.service.*.*(..))" ,这样切点才定位到方法上了。

  • 相关阅读:
    6.一个python-selenium的实战
    9.python-ini文件使用(读和写)
    4.表数据的操作-insert、delete
    5.数据库的查询-select
    2.自动化测试之python+selenium基础
    6.python中目录的操作
    sql如何先排序再去重
    hbase(0.94) get、scan源码分析
    Antlr 在 idea 中正确使用的方式
    某日看代码对代码可读性的思考
  • 原文地址:https://www.cnblogs.com/aaron911/p/7592508.html
Copyright © 2020-2023  润新知