zoukankan      html  css  js  c++  java
  • Spring基础知识之AOP的理解与应用

    AOP是什么?

      面向切面编程,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性的问题,这些横切性的问题与我们的业务逻辑关系不大,这些横切性的问题不会影响到主业务逻辑的实现,但是会散落到代码的各个部分,难以维护。AOP就是处理一些横切性的问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的,使代码的重用性的开发效率更高。

    APO的应用场景?

    1.   日志记录
    2.   权限验证
    3.   效率检查
    4.   事务处理
    5.   exception

    SpringAOP 和 AspectJ的关系?

      AOP是一种编程理念,SpringAOP和AspectJ都是AOP的实现,SpringAOP有自己的语法,但是语法过于复杂,所以SpringAOP借助了AspectJ的注解,但是底层还是自己的。

    SpringAOP提供的两种编程风格:

       1.使用@Aspect注解

       2.使用xml配置,aop:config命名空间

    SpringAOP支持AspectJ的步骤:

      1.启用@AspectJ注解:使用Java@Configuration声明配置类,添加@EnableAspectJAutoProxy注解启用@AspectJ支持

     1 /**
     2  * 使用注解实现aop代理
     3  * @author Administrator
     4  *
     5  */
     6 @Configuration
     7 @EnableAspectJAutoProxy
     8 @ComponentScan
     9 public class AOPAnnotation {
    10 
    11 }

      2.声明一个@AspectJ注释类,并且定义成一个bean交给spring容器管理。

     1 /**
     2  * 1.创建aop的切面类
     3  * @author Administrator
     4  *
     5  */
     6 @Component//首先加入spring容器中
     7 @Aspect//使用aspectj注解
     8 public class AspectjUtil {
     9 
    10     
    11 }

      3.声明一个pointCut,切入点表达式有@Pointcut注释表示,切入点声明由两部分组成,一个签名包含名称和任何参数,以及一个切入点表达式,该切入点表达式确定我们对哪个方法的执行感兴趣。

      声明切入点的几种方式(后面有详细讲解):

        1.execution,匹配方法执行连接点

        2.within,将匹配限制为特定类型中的连接点

        3.args,参数

        4.target,目标队形

        5.this,代理对象

     1 /**
     2  * 1.创建aop的切面类
     3  * @author Administrator
     4  *
     5  */
     6 @Component//首先加入spring容器中
     7 @Aspect//使用aspectj注解
     8 public class AspectjUtil {
     9 
    10     /**
    11      * 2.声明一个切点
    12      * @Description: TODO
    13      * @returnType: void
    14      */
    15     @Pointcut("execution(* org.wk.spring.dao.*.*(..))")//切入点表达式
    16     public void pointCut(){//切入点签名
    17         System.out.println("pointCut方法");
    18     }
    19 }

      4.声明一个Advice通知,advice通知和pointcut切入点表达式相关联,并在切入点匹配的方法执行@Before之前,@After之后或前后运行。

        通知类型:

          1.Before:连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常。

          2.After:连接点正常执行之后,执行过程中,正常执行结束后退出,非异常退出。

          3.After throwing:连接点抛出异常后执行

          4.After(finally):无论连接点是正常退出,还是异常退出,都会执行。

          5.Around:围绕连接点执行,例如方法调用,这是最有用的切面方式,around通知可以在方法调用之前和之后执行自定义行为。

     1 /**
     2  * 1.创建aop的切面类
     3  * @author Administrator
     4  *
     5  */
     6 @Component//首先加入spring容器中
     7 @Aspect//使用aspectj注解
     8 public class AspectjUtil {
     9 
    10     /**
    11      * 2.声明一个切点
    12      * @Description: TODO
    13      * @returnType: void
    14      */
    15     @Pointcut("execution(* org.wk.spring.dao.*.*(..))")//切入点表达式
    16     public void pointCut(){//切入点签名
    17         System.out.println("pointCut方法");
    18     }
    19     
    20     /**
    21      * 3.定义通知点
    22      * @Description: TODO
    23      * @returnType: void
    24      * 声明before通知,在pointcut切入点之前运行
    25      */
    26     @Before("pointCut()")
    27     public void before(){
    28         //编写通知的逻辑
    29         System.out.println("在pointCut方法之前");
    30     }
    31 }

    各个连接点Pointcut的意义?

      1.execution,用于匹配方法执行join points连接点,表达式的最小粒度是方法,在aop中最多使用。

        execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

        这里问号表示当前项可以有也可以没有,其中各项的语义如下

        modifiers-pattern:方法的可见性,如public,protected;
        ret-type-pattern:方法的返回值类型,如int,void等;
        declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
        name-pattern:方法名类型,如buisinessService();
        param-pattern:方法的参数类型,如java.lang.String;
        throws-pattern:方法抛出的异常类型,如java.lang.Exception;
        example:
        @Pointcut("execution(* com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的任意方法
        @Pointcut("execution(public * com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的public方法
        @Pointcut("execution(public * com.chenss.dao.*.*())")//匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法
        @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法
        @Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
        @Pointcut("execution(public * *(..))")//匹配任意的public方法
        @Pointcut("execution(* te*(..))")//匹配任意的以te开头的方法
        @Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任意的方法
        @Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法

      由于Spring切面粒度最小是表达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的信息,并且在spring中,大部分使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用范围是最为广泛的。

      2.within,表达式的最小粒度为类。

        @Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法

        @Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法
      within与execution相比,粒度更大,仅能实现到包和接口,类级别,而execution可以精确到方法的返回值,参数个数,修饰符,参数类型等。

      3.args,表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关。

        @Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
        @Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified
      args同execution不同的地方在于:
        args匹配的是运行时传递给方法的参数类型
        execution(* *(java.io.Serializable))匹配的是方法在声明时指定的方法参数类型。

       4.this,在使用JDK代理时,代理对象指向接口和代理类Proxy,使用cglib代理时,代理对象指向接口和子类(不使用Proxy)

      5.target,代理对象指向接口和子类

        此处需要注意的是,如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用的是CGLIB代理。

        JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口

        CGLIB继承被代理的类来实现

        所以使用target会保证目标不变,关联对象不会受到这个设置的影响。

        但是使用this时,会根据该选项的设置,判断时候能找到对象。

        @Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类

        @Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个

        @Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法

        @Pointcut("@within(com.chenss.anno.Chenss)")//等同于@target

    ProceedingJoinPoint 和JoinPoint的区别?

      ProceedingJoinPoint继承了JoinPoint,其中proceed()方法是aop代理链执行的方法。ProceedingJointPoint扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。

      JoinPoint的方法:

        1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表。

        2.Signature getSignature() :获取连接点的方法签名对象。

        3.java.lang.Object getTarget() :获取连接点所在的目标对象。

        4.java.lang.Object getThis() :获取代理对象本身。

        proceed()有重载,有个带参数的方法,可以修改目标方法的的参数

     1 /**
     2  * 1.创建aop的切面类
     3  * @author Administrator
     4  *
     5  */
     6 @Component//首先加入spring容器中
     7 @Aspect//使用aspectj注解
     8 public class AspectjUtil {
     9 
    10     /**
    11      * 2.声明一个切点
    12      * @Description: TODO
    13      * @returnType: void
    14      */
    15     @Pointcut("execution(* org.wk.spring.dao.*.*(..))")//切入点表达式
    16     public void pointCut(){//切入点签名
    17         System.out.println("pointCut方法");
    18     }
    19     
    20     @Around("pointCut()")
    21     public void around(ProceedingJoinPoint pjp){
    22         System.out.println("before around");
    23         //获取切点中方法的参数
    24         Object[] args = pjp.getArgs();
    25         if(args!=null && args.length>0){
    26             //对切入方法的参数进行逻辑处理
    27             for (int i = 0; i < args.length; i++) {
    28                 args[i]=args[i]+" world!";
    29             }
    30         }
    31         //执行环绕方法,把进行逻辑处理后的参数传进去
    32         try {
    33             pjp.proceed(args);
    34         } catch (Throwable e) {
    35             // TODO Auto-generated catch block
    36             e.printStackTrace();
    37         }
    38         System.out.println("after around");
    39     }
    40 }

      完成了对参数的逻辑处理。

     使用XML方式配置AOP:

      1.创建处理切面的类,定义处理方法

     1 /**
     2  * 使用xml配置aop的类
     3  * @author Administrator
     4  *
     5  */
     6 public class AOPXmlUtil {
     7     /**
     8      * 定义处理方法,与xml通知相对应
     9      * @Description: TODO
    10      * @returnType: void
    11      */
    12     public void before(){
    13         System.out.println("xml before");
    14     }
    15 }

      2.配置spring.xml文件,使用aop相关标签

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:p="http://www.springframework.org/schema/p"
     5     xmlns:c="http://www.springframework.org/schema/c"
     6     xmlns:aop="http://www.springframework.org/schema/aop"
     7     xmlns:context="http://www.springframework.org/schema/context"
     8     xsi:schemaLocation="http://www.springframework.org/schema/beans
     9         https://www.springframework.org/schema/beans/spring-beans.xsd
    10         http://www.springframework.org/schema/context
    11         https://www.springframework.org/schema/context/spring-context.xsd
    12         http://www.springframework.org/schema/aop
    13         https://www.springframework.org/schema/aop/spring-aop.xsd">
    14     <bean id="dao" name="dao" class="org.wk.spring.dao.impl.IndexDaoImpl">  
    15     </bean>
    16     <bean id="service" class="org.wk.spring.service.impl.IndexServiceImpl">
    17         <property name="dao" ref="dao"></property>
    18     </bean>
    19     <!--引入处理切面的类 ,类中提供切面之后执行的逻辑方法。 -->
    20     <bean id ="xmlAop" class="org.wk.spring.aop.AOPXmlUtil"></bean>
    21     <!--引入aop的标签,在xml中配置切面  -->
    22     <aop:config>
    23         <!--引入切点  -->
    24         <!-- aop:pointcut ID重复会出现覆盖,以最后出现的为准。不同aop:aspect内出现的pointcut配置,可以相互引用 -->
    25         <aop:pointcut expression="execution(* org.wk.spring.dao.*.*(..))" id="allDao"/>
    26         <!--创建aop切面处理类  -->
    27         <!-- aop:aspect ID重复不影响正常运行,依然能够有正确结果 -->
    28         <aop:aspect id="" ref="xmlAop">
    29             <aop:before method="before()" pointcut-ref="allDao"/>
    30         </aop:aspect>
    31     </aop:config>
    32 </beans>

      

  • 相关阅读:
    ubuntu18.04 安装 jdk
    MySQL集群(PXC)入门
    Synchronized 详解
    Java多线程之内存可见性
    MySQL8.0新特性
    vue配置域名访问
    debain8 安装mysql8
    idea 激活步骤
    spring boot 设置tomcat post参数限制
    人脸识别技术全面总结:从传统方法到深度学习[转载自机器之心]
  • 原文地址:https://www.cnblogs.com/wk-missQ1/p/12416688.html
Copyright © 2011-2022 走看看