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>

  

原文地址:https://www.cnblogs.com/wk-missQ1/p/12416688.html