浅谈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.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</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
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~的更多相关文章
- 基于SpringBoot AOP面向切面编程实现Redis分布式锁
基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...
- Spring(4)——面向切面编程(AOP模块)
Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用. AOP 即 Aspect Orien ...
- 详细解读 Spring AOP 面向切面编程(二)
本文是<详细解读 Spring AOP 面向切面编程(一)>的续集. 在上篇中,我们从写死代码,到使用代理:从编程式 Spring AOP 到声明式 Spring AOP.一切都朝着简单实 ...
- Spring:AOP面向切面编程
AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...
- Spring Boot2(六):使用Spring Boot整合AOP面向切面编程
一.前言 众所周知,spring最核心的两个功能是aop和ioc,即面向切面和控制反转.本文会讲一讲SpringBoot如何使用AOP实现面向切面的过程原理. 二.何为aop aop全称Aspec ...
- 谈一谈AOP面向切面编程
AOP是什么 : AOP面向切面编程他是一种编程思想,是指在程序运行期间,将某段代码动态的切入到指定方法的指定位置,将这种编程方式称为面向切面编程 AOP使用场景 : 日志 事务 使用AOP的好处是: ...
- spring框架学习(三)——AOP( 面向切面编程)
AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...
- Spring 08: AOP面向切面编程 + 手写AOP框架
核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...
随机推荐
- 值类型不允许赋值为Null
与引用类型不同,值类型不可能包含 null 值. 每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值.有关值类型默认值的信息,请参见默认值表. bool false byte 0 char ' ...
- windows下将jar文件设置为系统服务
jar文件的执行需要java环境,怎么配置环境相信不用说了 因为不想每次开机都手动启动一次程序,那么我们就需要把它配置成开机自启动的服务,下面就来讲一种方法 首先,我们知道jar文件的执行命令为 ja ...
- Tomcat部署项目的三个方法
所需软件安装 要想在Tomcat中部署项目前提是先要搭建好Tomcat,搭建Tomcat就离不开以下软件包的安装配置,本次演示使用Linux平台 1.JDK软件包 JDK是一切java应用程序的基础, ...
- AutoCAD 2019 for mac 非常好用的CAD三维设计绘图软件
macOS下用什么cad软件?mac在哪下载cad软件? AutoCAD 2019 for mac 是一款非常好用的CAD三维设计绘图软件,可应用三维建模.CAD.渲染.动画.视觉特效和数字图像. A ...
- axios学习和使用
网络请求的方式 传统的Ajax,基于XMLHttpRequest(不推荐) 配置调用方式混乱(回调地狱) jQuery-Ajax (在vue开发中不推荐) 相对于传统的Ajax非常好用 但是jQuer ...
- CVE-2019-0708(非蓝屏poc)远程桌面代码执行漏洞复现
玩了几天 刚回成都 玩电脑复现一下~ 内核漏洞原理暂时 没看懂 别问 ,问就是不懂 0x01 复现环境和Exp准备 漏洞影响范围 Windows 7 Windows Server 2008 R2 W ...
- msf后门之persistence
在获取得了meterpreter shell后 使用Persistence建立持续性后门 run persistence -h meterpreter > run persistence -h ...
- vi/vim编辑器使用方法详解
vi编辑器是所有Unix及Linux系统下标准的编辑器,他就相当于windows系统中的记事本一样,它的强大不逊色于任何最新的文本编辑器.他是我们使用Linux系统不能缺少的工具.由于对Unix及li ...
- PHP 插入排序 -- 折半查找
1. 折半查找 -- Binary Insertion Sort 时间复杂度 : O(n^2) 适用条件 : 相对直接插入排序,减少了数值的比较次数.适用于需要排序的数码比较少的情况. <?p ...
- Cocos2d-x 学习笔记(21.1) ScrollView “甩出”效果与 deaccelerateScrolling 方法
1. 简介 “甩出”效果是当我们快速拖动container并松开后,container继续朝原方向运动,但是渐渐减速直到停止的效果. ScrollView的onTouchEnded方法会设置Timer ...