Spring AOP

1.什么是Spring AOP?

我们先了解以下什么是AOP。AOP(Aspect Oriented Programming): 面向切面编程,它是一种思想,它是对一类事情的集中处理。比如:我们在创建购物网站的时候,我们的查看商品,购买商品等页面需要进行登录校验,我们可以把每个页面进行登录校验的操作进行集中的处理。Spring AOP是AOP的一种实现形式。

2.为什么要用AOP?

对于像用户登录校验的功能,在一个程序的很多地方会使用到,我们可以考虑AOP来进行统一的处理。

除了统一的用户登录判断以外,AOP还可以实现:

  • 统一日志记录
  • 统一方法执行时间统计
  • 统一的返回格式设置
  • 统一的异常处理
  • 事务的开启和提交等

3.Spring AOP应当怎么学?

Spring AOP学习主要分为以下三个部分:

  1. 学习AOP是学习如何组成的?也就是AOP组成的相关概念
  2. 学习Spring AOP使用
  3. 学习Spring AOP实现原理

3.1AOP组成

切面】

切面由切点和通知组成,它既包含了横切逻辑的定义,也包含了连接点的定义。

连接点】

用用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常时,甚至修改字段时

前面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

连接点相当于需要增强的某个AOP功能的所有方法

【切点】

作用是提供一组规则来匹配连接点,满足规则的连接点添加通知。

通知】

切面也是有它们的目标的——它必须完成它的任务,这个任务被称为通知

通知:定义了切面是什么,何时使用,以及描述了切面要完成的工作,还解决了何时执行这个工作的问题。

Spring切面类中,可以在方法上使用以下数据,会设置方法为通知方法,在满足条件后会通知本方法进行调用:

  • 前置通知使用@Before:通知方法会在目标方法执行之前执行。
  • 后置通知使用@After:通知方法会在目标方法之后或抛出异常之后执行。
  • 返回之后通知使用@AfterReturning:通知方法会在目标方法返回后调用。
  • 抛异常后通知使用@AfterThrowing:通知方法会在目标方法抛出异常后执行。
  • 环绕通知使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行执行自定义行为。

Spring AOP_第1张图片

 3.2Spring AOP实现

接下来,我们使用Spring AOP来实现下AOP的功能,完成的目标是拦截所有UserController里面的方法,每次调用UserController中任意一个方法时,我们执行相应的通知事件。

Spring AOP的实现步骤如下:

  1. 添加Spring AOP框架支持
  2. 定义切面和切点
  3. 定义通知

3.2.1添加AOP框架支持

在pom.xml中添加如下配置:



 org.springframework.boot
 spring-boot-starter-aop

3.2.2定义切面和切点

切点指的是具体要处理的一类问题,比如用户登录权限的校验,就是一个具体的问题,记录所有方法执行日志就是一个具体的问题,切点定义的是一类问题。

我们先准备下,接下来会用到的代码

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/hi")
    public String getHi() {
        return "hi";
    }
    
    @RequestMapping("/name") 
    public String getName() {
        return "name";
    }
        
    @RequestMapping("password")
    public String getPassword() {
        return "password";
    }
}

Spring AOP切点定义如下,在切点中我们要定义拦截的规则,具体实现如下:

@Aspect //定义此类是是一个切面
@Component
public class UserAspect {
    //定义切点,这里使用了AscpectJ表达式语法
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut(){}
}

切点表达式说明:

  • *:匹配任意字符,只能匹配一个元素(包,类,或方法,方法参数)
  • ..:匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用
  • +:表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.example.Car+,表示继承该类的所有子类包括类本身

切点表达式由切点函数组成,其中execution()是常见的切点函数,语法为:
execution(<修饰符><返回类型><包.类.方法(参数)><异常>)

  • 修饰符和异常一看可以省略,*表示任意修饰符
  • 返回值不能省略,void表示没有返回值,*表示任意返回值
  • 包,com.gyf.crm表示固定包,com.gyf.crm.*.service表示crm下面子包任意service包
  • 类,UserController表示指定类,*lmpl表示以lmpl结尾的类,User*表示以User开头的类,*表示任意类
  • 方法名,add*表示以add开头的任意方法,*表示任意方法
  • 参数,()表示无参,(..)表示任意参数
  • throws,可以省略,一般不写
     

表达式实例:

execution(* com.cad.demo.User.*(..)):匹配User类下的所有方法;

execution(* com.cad.demo.User+.*(..):匹配User类的子类的所有方法;

3.2.3定义相关通知

通知定义的是被拦截的方法具体要执行的业务,比如用户登录权限验证方法。Spring AOP中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件以后会通知本方法进行调用:

  • 前置通知使用@Before:通知方法会在目标方法调用之前执行
  • 后置通知使用@After:通知方法会在目标方法返回或者抛出异常后调用
  • 返回后通知使用@AfterReture:通知方法会在目标方法返回后调用
  • 抛出异常通知使用@AfterThrowing:通知方法会在目标方法抛出异常后调用
  • 环绕通知使用@Around:通知包裹了被通知的方法,会在被通知的方法之前和调用之后执行自定义的行为

具体实现如下:

@Aspect //定义此类是是一个切面
@Component
public class UserAspect {
    //定义切点,这里使用了AscpectJ表达式语法
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut(){}

    //前置通知
    @Before("pointcut()")
    public void doBefore() {
        System.out.println("执行了Before方法");
    }

    //后置通知
    @After("pointcut()")
    public void doAfter() {
        System.out.println("执行了After方法");
    }

    //return之前通知
    @AfterReturning("pointcut()")
    public void doAfterReturn() {
        System.out.println("执行了AfterReturn方法");
    }

    //抛出异常之前通知
    @AfterThrowing("pointcut()")
    public void doAfterThrowing() {
        System.out.println("执行了AfterThrowing方法");
    }

    //添加环绕方法
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object obj = null;
        System.out.println("Around方法开始执行");

        try {
            //执行拦截方法
            obj=joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("Around方法执行结束");
        return obj;
    }
}

Spring AOP_第2张图片

Spring AOP_第3张图片

 

 3.3Spring AOP实现原理

Spring AOP是构建在动态代理基础上,因此Spring对AOP的支持局限于方法级别的拦截。

Spring AOP支持 JDK Proxy和CGLIB方法实现动态代理。默认情况下,实现了接口的类,使用AOP会基于JDK生成代理类,没有实现接口的类,会基于CGLIB类

JDK和CGLIB实现的区别

  1. JDK实现,要求被代理的类必须实现接口,之后通过InvocationHandler以及Proxy,在运行时生成动态的在内存中生成了代理类对象,该对象是通过实现同样的接口实现,只是代理类是在运行期间,动态的织入统一的业务逻辑字节码来完成。
  2. CGLIB实现,被代理类可以不实现接口,是通过继承被代理类,在运行时动态的生成代理类对象

4.总结

AOP是对某方面能力的统一实现,它是一种思想,Spring AOP是对AOP的具体实现

Spring AOP的实现步骤:

  1. 添加AOP框架支持
  2. 定义切面和切点
  3. 定义通知

Spring AOP是通过动态代理的方式,在运行期间将AOP代码织入到程序中,它的实现方式有两种:JDK Proxy和CGLIB

你可能感兴趣的:(spring,boot,后端)