Spring AOP

AOP

  • 面向切面编程

  • 做无侵入式功能增强的

核心概念

  • 连接点(JoinPoint):就是所有方法

  • 切入点(Pointcut) :是给需要增强的方法(一个表达式)

  • 通知(Advice) :增强功能的方法

  • 通知类 :增强方法所属的类

  • 切面(Aspect) :描述的是增强的方法和要增强的方法之间的关系(通知和切入点之间的关系)

  • 目标对象:Aop在运行程序的时候时代理对象在运行,这个代理对象是对原始对象做的代理,这个原始对象就叫做目标对象。

  • 代理:整个AOP的实现过程就是用代理模式进行的。

AOP的工作流程

  1. Spring容器启动

  2. 读取所有切面配置的切入点

  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点(判定有没有切入点和通知有没有匹配成功)

    • 匹配失败,创建对象

    • 匹配成功,创建原始对象(目标对象)的代理对象

  4. 获取bean执行方法

    • 获取bean,调用方法并执行,完成操作

    • 获取bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

切入点表达式

  • *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配出现

    任意返回值,任意包中的UserService的类或者接口,带有save的所有方法,一个参数的方法
    execution ( public * com.yang.*.UserService.save*(*))
  • .. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

    任意包,任意参数
    execution ( public User com..UserService.saveById(..))
  • + :专用于匹配子类类型

    execution(**..*Service+.*(..))

书写技巧

  • 描述切入点通常描述接口,而不描述实现类,(解耦)

  • 访问修饰符可以省略(接口都是public的)

  • 查询类使用*通配符匹配,增删改使用精准类型加速匹配

  • 包名书写尽量不使用..匹配,效率太低,常用*做单个包描述匹配,或精准匹配

  • UserService 书写成*Service,绑定业务层接口名

  • getById 书写成getBy*

  • 参数较为复杂的时候,根据业务灵活调整

  • 通常不使用异常作为匹配规则

上代码

aop

package com.yang.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//@Component
//@Aspect//这个注解是来说明这是个切面
public class MethodAop {

   @Pointcut("execution(void com.yang.dao.Impl.UserDaoImpl.save())")//切入点,在这个方法上执行增强
   public void pc(){}

   @Before("pc()")//这个就是个切面,在切入点之前执行
   public void method(){//增强的方法
       System.out.println(System.currentTimeMillis());
  }
   /*
    1.环绕通知必须依赖ProceedingJoinPoint pjc 形参才能实现对原始方法的调用,进而实现原始方法调用的前后实现增强
    2.如果未使用ProceedingJoinPoint ,则跳过对原始方法的执行
    3.对原始方法的调用,可以不接受返回值,设置成void,如果有的话就必须设置成Object类型
    4.如果原始方法的返回值是void的,则通知方法的返回值可以设置成void也可以设置成Object类型。
    5.pjc.proceed()会抛出异常,因为无法预知原始方法是否会抛出异常。
    */
   @Around("pc()")
   public Object around(ProceedingJoinPoint pjc) throws Throwable {
       System.out.println("before...");
       Object proceed = pjc.proceed();
       System.out.println("after...");
       return proceed;
  }
}

Method1

package com.yang.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MethodAop1 {
   //增强所有业务层
   @Pointcut("execution(* com.yang.service.*Service.*(..))")
   public void pc(){}

   @Around("pc()")
   public void round(ProceedingJoinPoint pjp ) throws Throwable {
       //获取执行签名
       Signature sig = pjp.getSignature();
       String typeName = sig.getDeclaringTypeName();
       String name = sig.getName();

       long start = System.currentTimeMillis();
       for (int i = 0; i <10000 ; i++) {
           pjp.proceed();
      }
       long end = System.currentTimeMillis();

       System.out.println("万次执行:"+typeName+"."+name+"---->"+(end-start)+"ms");
  }
}

SpringConfig

package com.yang.config;

import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.yang")
@PropertySource("jdbc.properties")
@Import({SpringDataSource.class,MybatisConfig.class})
@EnableAspectJAutoProxy//这个注解是来开启@Aspect注解的
public class SpringConfig {
}

 

Aop数据处理

DataHandleAd.java

package com.yang.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class DataHandleAd {

   @Pointcut("execution(boolean com.yang.dao.*Dao.*(..))")
   public void dataSource(){}

   @Around("DataHandleAd.dataSource()")
   public Object dataHandle(ProceedingJoinPoint pjp ) throws Throwable {
       //得到原始数据的参数数组
       Object[] args = pjp.getArgs();
       for (int i = 0; i < args.length; i++) {
           //判断是否是字符串
           if (args[i].getClass().equals(String.class)) {
               //去掉空格
               args[i] = args[i].toString().trim();
          }
      }
       //将处理后的参数给原始方法
       return pjp.proceed(args);
  }
}

pom.xml

<dependencies>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>5.2.10.RELEASE</version>
       </dependency>
       <dependency>
           <groupId>org.aspectj</groupId>
           <artifactId>aspectjweaver</artifactId>
           <version>1.9.4</version>
       </dependency>
   </dependencies>