浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~

简介: 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~

简介

我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转、依赖注入、以及AOP切面。当然AOP作为一个Spring

的重要组成模块,当然IOC是不依赖于Spring框架的,这就说明你有权选择是否要用AOP来完成一些业务。

AOP面向切面编程,通过另一种思考的方式,来弥补面向对象编程OOP当中的不足,OOP当中最重要的单元是类,所以万物皆对象,万物皆是

对象类。而在AOP的模块单元中,最基础的单元是切面,切面对切点进行模块化的管理。

最后再提一句:Spring当中的AOP是利用Java的代理模式实现的

AOP概念

让我们从一些基础的术语开始了解面向切面编程AOP,术语不是特别的直观,最好的方式就是通过文本理解+图像理解+代码实例理解

这样对于我们来说才是真正意义上的理解。

  • 切面:(Aspect) 一个关注点的模块化,就比较笼统的一个概念,关注点可能横切多个对象。若不理解请往后看图片理解,对应的注解有@Aspect。
  • 连接点:(Joinpoint) 在程序执行过程中某个特定的点,一个连接点总是代表一个方法的执行。
  • 通知:(Advice) 通知表示在一个连接点执行的具体的动作,比如After Before 表明通知的具体动作
  • 切入点:(Pointcut)通过一个表达式去表明我所定义的通知在那个地点具体执行。
  • 前置通知:(Before advice)表明在连接点执行之前执行的动作。
  • 后置通知:(After returning advice)在某个连接点完成后的通知,比如一个方法没有抛出任何异常,正常返回。
  • 环绕通知:(Around Advice) 环绕可以看作是包含前置通知和后置通知的一个通知,先了解,后面具体理解。
  • 异常通知:(After throwing advice) 在方法异常推出时候执行的通知。
  • 最终通知:(After advice) 在连接点退出时候执行的通知。不论是正常退出还是异常退出。

说了这么多,都感觉迷迷糊糊的,我们首先来看一个例子吧,通过这个例子来理解AOP切面,通过例子在具体说明

Springboot AOP

版本信息:Springboot 2.1.6

添加依赖 Web AOP

<dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-aopartifactId>
        dependency>

写一个最简单的控制器

@RestController
@RequestMapping("/")
public class AuthController {
    private Logger logger = LoggerFactory.getLogger(AuthController.class);
    @GetMapping("login")
    public String login(String user,String pass) {
        logger.info("login--->user",user);
        logger.info("login--->pass",pass);
        return "success";
    }
}

这样的控制器在 SpringBoot的Web开发当中是很常见的,收到前端的请求,将参数进行校验,我们这里为了简单,不作校验,只是打印出来,为了配合我们后面的AOP的理解而

做的一个最简单的控制器,测试请求以下路径,控制台打印以下内容

http://localhost:8080/login?user=root&pass=root
2019-11-09 09:59:00.585  INFO 11100 --- [nio-8080-exec-1] c.e.demo.controller.AuthController       : login--->user
2019-11-09 09:59:00.588  INFO 11100 --- [nio-8080-exec-1] c.e.demo.controller.AuthController       : login--->pass

定义切面类

1、定义一个切面类,加入@Aspect注解和@Component注解

@Aspect
@Component
public class WebLogAsp {}
@Aspect 注解将找个类定义为一个切面对象,通过@Component注解将这个类对象注入到IOC容器,交给Spring来进行管理。

2、定义一个切入点 通过@Pointcut

@Pointcut("execution(public * com.example.demo.controller.*.*(..))")
    public void controllerLog(){}
@Pointcut这个注解主要用来定义切入点,通过表达式的方式,来告诉Spring,我这个切点要切到什么位置,常用的就是execution去匹配连接点。

主要来说一下execution 匹配表达是的表达方法,我们按照以下的例子来说明:

@Pointcut("execution(public * com.example.demo.controller.*.*(..))")

语法:execution( [方法修饰符(可选)]__返回类型__类路径__方法名__(参数)__[异常模式(可选)] )

这里我用下划线来代替空格,比较直观的可以看出,我们这个例子里面,我将这个切点切入到com.example.demo.controller包下所有类的所有方法上面。

*就是通配符,(..)代表任意多个参数,也就说明我切入到的方法它的参数我是不限定的,可以有任意个参数。

3、定义通知项,@Before定义一个前置通知

@Before("controllerLog()")
public void beforeAdvice(JoinPoint joinPoint){}
@Before注解传入字符串方法名,也就是我们上面定义的切入点的方法名,告诉它这个通知是在切入点 controllerLog()上面执行的通知,它会在切入点方法执行之前率先执行

这个方法传入了一个JoinPoint 对象,也就是我们所说的连接点对象,连接点可以理解为切入点方法执行时候所产生的一个对象。

这里有几个具体的方法很重要,需要说明一下:

Object[] getArgs();获取切入点方法执行时候传入的参数。

Object getTarget();获取的是切入方法所在的类对象,简单理解例子里面的切入点是login()方法,所以返回的对象就是AuthController对象

Object getThis();返回AOP框架为目标对象生成的代理对象。

这里将详细代码举例,通过打印的方式进行输出,通过SpringMvc的RequestContextHolder获取线程的请求对象,这里我们可以拿到当前请求

的一些具体参数,比如访问人的IP信息,它所请求的URL以及请求所带的参数等待。

具体请参考:https://www.runoob.com/servlet/servlet-client-request.html

@Before("controllerLog()")
    public void beforeAdvice(JoinPoint joinPoint){
        logger.info("前置通知开始--->");
        /*获取当前请求的HttpServletRequest*/
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        logger.info("URL-->"+request.getRequestURL().toString());
        logger.info("IP-->"+request.getRemoteAddr());
        logger.info("HTTP_Method-->"+request.getMethod());
        logger.info("Request_args-->"+ Arrays.toString(joinPoint.getArgs()));
        logger.info("前置通知结束--->");
    }

4、环绕通知方法 @Around

首先来看一部分代码,这里传递了一个 ProceedingJoinPoint 对象,它是JoinPoint 切点对象的一个子类,也可以为它是切点所在的方法执行时候所产生的一个对象

这里最重要的一个方法当属于 proceed() ;执行proceed()就表示执行切点所在的方法执行,它会返回一个Object对象,也就是目标方法所返回的对象。

@Around("controllerLog()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        logger.info("环绕通知开始-->");
        Object result = null;
        /*proceed()方法表示连接点方法执行 result 为连接点方法的返回值*/
        result = proceedingJoinPoint.proceed();
        logger.info("环绕通知返回值-->" + result);
        logger.info("环绕通知结束-->");
        return result;
    }

踩坑:环绕方法一定要将result对象返回出去,若定义为void 无返回值的话,将在后面的@AfterReturning 后置通知中无法取到值!!!!

5、后置通知 @AfterReturning 连接点正常执行完毕后的通知

@AfterReturning(value = "controllerLog()",returning = "obj")
    public void afterReturning(JoinPoint joinPoint,Object obj){
        logger.info("后置通知正常返回-->"+obj);
    }

这里传递了两个参数,第一个参数还是指定切点,第二个参数指定的是返回值,这个返回值就是连接点所返回的参数值,需要在环绕通知里面将其返回出来才可以

取到值,不然返回的就是Null

6、异常通知 @AfterThrowing

@AfterThrowing(value = "controllerLog()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Exception ex){
        logger.info("异常通知-->"+ex.getMessage());
    }

这里和上面的后置通知差不多,不过第二个参数是一个异常类型对象,可以取出发生异常时候的异常信息。

7、最终通知 @After

这个和@Before一样,不再细说!

正常返回测试

这里可以很明确的看到,一个正常的返回路径,相信我不说大家都可以看的清楚,画图最清楚的,接收到请求后,首先工作的是环绕通知,

环绕通知里面执行proceed()方法后,才会进入连接点方法执行,Before是在连接点方法执行之前所执行的,而后执行连接点方法内容,

正常执行完后,不发生异常情况,环绕通知结束,就会执行最终通知After After执行完毕后,才会执行后置通知AfterReturning

异常情况测试返回

我们在控制器里面模拟进行一个异常的抛出,看一下执行的顺序

logger.info("将要抛出异常");
        int a = 1/0;

可以很明明显的看到,我们在控制器里面通过一个除0的操作,进行抛出一个异常,这里环绕通知是没有执行完毕的,因为抛出异常,停止了运行

从接收到请求开始,进入环绕通知,然后环绕通知里面调用了proceed()方法,其实就是让连接点的方法开始运行,这时候前置通知首先跑起来,可以看到

前置通知是完完全全走完的。前置通知完毕后,下来是连接点的方法运行起来了,这里因为抛出了异常,没有进行捕获,最终通知还是一样正常执行,不过最后执行的是异常的通知,

而不是像上面一样。这里不同的地方还是要多进行理解。

画图理解

1、理解切面、切入点、连接点、通知、目标对象

2、理解通知点正常与异常执行顺序

小结

通过对SpringAOP框架的研究,以及画图的理解,能够更深刻的理解里面运行时候的内层含义,以及AOP的一些应用,比如系统里面的日志系统,通过学习AOP

完完全全可以很轻松的通过切面,将需要记录的日志信息存到 数据库, 节约大量时间。

参考:

https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html

http://shouce.jb51.net/spring/aop.html

https://www.cnblogs.com/wangshen31/p/9379197.html

代码:

https://gitee.com/mrc1999/Springboot-aop



相关文章
|
2月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
383 0
|
1月前
|
监控 Java Spring
AOP 切面编程
AOP(面向切面编程)通过动态代理在不修改源码的前提下,对方法进行增强。核心概念包括连接点、通知、切入点、切面和目标对象。常用于日志记录、权限校验、性能监控等场景,结合Spring AOP与@Aspect、@Pointcut等注解,实现灵活的横切逻辑管理。
345 6
AOP 切面编程
|
30天前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
6月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
|
3月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。
|
3月前
|
监控 Java Spring
AOP切面编程快速入门
AOP(面向切面编程)通过分离共性逻辑,简化代码、减少冗余。它通过切点匹配目标方法,在不修改原方法的前提下实现功能增强,如日志记录、性能监控等。核心概念包括:连接点、通知、切入点、切面和目标对象。Spring AOP支持多种通知类型,如前置、后置、环绕、返回后、异常通知,灵活控制方法执行流程。通过@Pointcut可复用切点表达式,提升维护性。此外,结合自定义注解,可实现更清晰的切面控制。
313 5
|
3月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
3月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
7月前
|
人工智能 监控 Java
面向切面编程(AOP)介绍--这是我见过最易理解的文章
这是我见过的最容易理解的文章,由浅入深介绍AOP面向切面编程,用科普版和专家版分别解说,有概念,有代码,有总结。
|
9月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
582 25