当前位置: 首页 > news >正文

【Java EE进阶 --- SpringBoot】Spring 核心 --- AOP


🚀 欢迎来到我的CSDN博客:Optimistic _ chen
一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!


🚀我的专栏推荐:

专栏内容特色适合人群
🔥C语言从入门到精通系统讲解基础语法、指针、内存管理、项目实战零基础新手、考研党、复习
🔥Java基础语法系统解释了基础语法、类与对象、继承Java初学者
🔥Java核心技术面向对象、集合框架、多线程、网络编程、新特性解析有一定语法基础的开发者
🔥Java EE 进阶实战Servlet、JSP、SpringBoot、MyBatis、项目案例拆解想快速入门Java Web开发的同学
🔥Java数据结构与算法图解数据结构、LeetCode刷题解析、大厂面试算法题面试备战、算法爱好者、计算机专业学生

🚀我的承诺:
✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例

✅ 持续更新:专栏内容定期更新,紧跟技术趋势

✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)


🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨

📌 标签:#技术博客  #编程学习  #Java  #C语言  #算法  #程序员

文章目录

  • 前言
  • AOP思想
  • 初学AOP
    • 准备工作
    • Spring AOP详解
      • 切点(Pointcut)
      • 连接点(Join Point)
      • 通知(Advice)
      • 切面(Aspect)
      • 通知类型
    • @PointCut
    • 切面优先级@Order
    • 切点表达式
      • execution表达式
      • @annotation
        • 编写自定义注解@MyAspect
        • 使用@annotation表达式
  • 完结撒花!🎉

前言

前面学习过Spring的第一大核心Spring-Ioc,受到众多读者访问,而今天要了解的AOP甚至比它更加抽象,难以理解。

AOP思想

AOP全称:Aspect Oriented Programming(面向切面编程);切面:指某一类特定问题(后面会系统学习)。所以:AOP也可以理解为面向特定方法编程的思想。

核心思想:将业务逻辑与横切关注点分离,集中解决某一类问题,也就是上篇博客写的统一处理问题,就是AOP思想的具体实现。

Spring AOP:AOP是一种思想,而Spring框架实现了这种思想。

初学AOP

我们会通过一个例子来体验一下Spring AOP的使用:实现一个功能记录各个接口方法的执行时间。
正常情况下,我们首先考虑的就是在方法运行前和运行后,记录下开始时间和结束时间,计算二者之差即可。

 long startTime=System.currentTimeMillis();//执行方法long endTime=System.currentTimeMillis();log.info("方法耗时:"+(endTime-startTime)+" ms");

这个方法可以,但是如果我在一个项目中要测超级多的接口方法耗时,那意味着我要去给每一个方法写这段代码,工作量太大。
那这时就需要AOP来解决:AOP在程序运行期间在不修改源代码的基础上对已有方法进行增强。

准备工作

引入AOP依赖:
在项目中引入Spring AOP的依赖,在pom.xml中添加:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写记录每个方法耗时的程序:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;import javax.naming.event.ObjectChangeListener;@Slf4j
@Component
@Aspect
public class TimeAspect {/*** 记录方法耗时*/@Around("execution(* com.zc.blog.controller.*.*(..))")public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {long startTime=System.currentTimeMillis();Object result=pjp.proceed();long endTime=System.currentTimeMillis();log.info("方法耗时:"+(endTime-startTime)+" ms");return result;}
}

在这里插入图片描述
@Aspect:标识这是⼀个切⾯类
@Around: 环绕通知,在⽬标⽅法的前后都会被执⾏ . 后⾯的表达式表⽰对哪些⽅法进⾏增强
ProceedingJoinPoint.proceed() 让原始⽅法执⾏

总结:通过上面的实例,我们发现AOP编程的优点
1.代码解耦性强:无入侵性,在不修改原始代码的基础上就可以对原始方法进行增强
2.减少代码的重复率
3.维护方便

Spring AOP详解

切点(Pointcut)

切点,也称为"切入点":提供一组规则(切点表达式语言),告诉程序对哪些方法进行增强。也就是告诉程序被增强方法的路径
在这里插入图片描述

连接点(Join Point)

满足切点表达式规则的方法,就是连接点,也就是能被AOP控制的方法。

实例中,controller包中的所有方法都是连接点。
比如:

切点表达式:计算机学院所有学生。 连接点:张三 、小王、小赵、小段等学生

通知(Advice)

通知不是我们理解的”通知“,这里指:具体要完成的工作,那些重复的逻辑,也就是功能增强部分(记录耗时时间的方法)
在这里插入图片描述
在AOP面向切面编程中,这部分重复的逻辑代码单独定义,这部分代码就是通知的内容。

切面(Aspect)

切面(Aspect)=切点(Pointcut)+ 通知(Advice)
通过公式就知道,切面是通过切点完成通知;也是当前AOP程序针对哪些方法(切点),在什么时候执行什么操作(通知)
在这里插入图片描述

注意:切⾯所在的类,我们⼀般称为切⾯类(被@Aspect注解标识的类)

通知类型

Spring AOP提供了五种通知类型

  • 前置通知(@Before):在目标方法执行之前执行
  • 后置通知(@After):在目标方法执行之后执行,无论方法是否正常返回
  • 返回通知(@AfterReturning):在目标方法正常返回之后执行
  • 异常通知(@AfterThrowing):在目标方法抛出异常时执行
  • 环绕通知(@Around):围绕目标方法执行,可以在方法执行前后自定义逻辑

我们通过代码来显式的观察这几种类型:

@Slf4j
@Component
@Aspect
public class AspectDemo {//前置通知@Before("execution(* com.zc.blog.controller.*.*(..))")public void doBefore(){log.info("执行Before方法");}//后置通知@After("execution(* com.zc.blog.controller.*.*(..))")public void doAfter(){log.info("执行After方法");}//返回后通知@AfterReturning("execution(* com.zc.blog.controller.*.*(..))")public void doAfterReturning(){log.info("执行AfterReturning方法");}//抛出异常后通知@AfterThrowing("execution(* com.zc.blog.controller.*.*(..))")public void doAfterThrowing(){log.info("执行AfterThrowing方法");}//添加环绕通知@Around("execution(* com.zc.blog.controller.*.*(..))")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("方法执行前执行");Object result=joinPoint.proceed();log.info("方法结束后执行");return result;}
}

在这里插入图片描述
程序运行逻辑:
在这里插入图片描述
方法抛出异常时,表示方法逻辑有误,通知中的环绕后的代码逻辑也不会执行,因为@Around环绕通知后需要调用joinPoint.proceed()方法。

注意:观察代码发现,一个切面类可以有多个切点

@PointCut

我们发现上面代码的切点全部相同,这样代码中存在大量重复,Spring提供的@PointCut注解可以把公共的切点提取出来,需要时引入该切点即可。(类似于数学中的乘法分配律)

@Slf4j
@Component
@Aspect
public class AspectDemo {@Pointcut("execution(* com.zc.blog.controller.*.*(..))")private void pt(){};//前置通知@Before("pt()")public void doBefore(){log.info("执行Before方法");}//后置通知@After("pt()")public void doAfter(){log.info("执行After方法");}...

同时目前切点定义只能在当前切面类中使用,如果在其他切面类中使用,就需要把private改为public ,引用方式为:全限定类名.方法名()

@Before("com.zc.blog.Demo.AspectDemo.pt()")
public void doBefore(){log.info("执行Before方法");}

切面优先级@Order

当我们在项目中定义多个切面类时,并且这些切面类中有多个切点都匹配到同一个方法,当这个方法运行时,这些切面类中的通知都会运行,那么它们的运行顺序是什么呢?
多创建几个切面类测试:
在这里插入图片描述
发现规律:

  • @Before通知:排名靠前的先执行
  • @After通知:排名靠后的后执行

但是这种情况不受我们程序员掌控,为了提高我们对代码的控制,使用@Order注解:现在我们按照默认的倒序运行

@Slf4j
@Component
@Aspect
@Order(1)
public class AspectDemo4 {@Pointcut("execution(* com.zc.blog.controller.*.*(..))")private void pt(){};//.....
}@Slf4j
@Component
@Aspect
@Order(2)
public class AspectDemo3 {@Pointcut("execution(* com.zc.blog.controller.*.*(..))")private void pt(){};//.....
}@Slf4j
@Component
@Aspect
@Order(3)
public class AspectDemo2 {@Pointcut("execution(* com.zc.blog.controller.*.*(..))")private void pt(){};//.....
}

在这里插入图片描述
使用@Order注解执行顺序是:

  • @Before通知:数字越小先执行
  • @After通知:数字越大后执行

总结:@Order控制切⾯的优先级,先执⾏优先级较⾼的切⾯,再执⾏优先级较低的切⾯,最终执⾏⽬标⽅法

切点表达式

常见的表达式有两种:

  1. execution(......): 根据方法的签名来匹配
  2. @annotation(...): 根据注解匹配

execution表达式

语法规则:
execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)
在这里插入图片描述

@annotation

匹配多个类的方法,execution表达式实现比较复杂,我们借助自定义注解的方式以及 切点表达式@annotation 来描述这个场景

编写自定义注解@MyAspect
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}

简单解释一下两个注解:
@Target 标识了Annotation 所修饰的对象范围,即该注解可以⽤在什么地⽅

ElementType.TYPE: ⽤于描述类、接⼝(包括注解类型)或enum声明
ElementType.METHOD:描述⽅法
ElementType.PARAMETER:描述参数
ElementType.TYPE_USE: 可以标注任意类型

@RetentionAnnotation被保留的时间长短,表示注解的生命周期

RetentionPolicy.RUNTIME运⾏时注解.表⽰注解存在于源代码,字节码和运⾏时中.这意味着在编译时,字节码中和实际运⾏时都可以通过反射获取到该注解的信息.通常⽤于⼀些需要在运⾏时处理的注解
RetentionPolicy.CLASS编译时注解.表⽰注解存在于源代码和字节码中,但在运⾏时会被丢弃.这意味着在编译时和字节码中可以通过反射获取到该注解的信息,但在实际运⾏时⽆法获取.通常⽤于⼀些框架和⼯具的注解
RetentionPolicy.SOURCE:表⽰注解仅存在于源代码中,编译成字节码后会被丢弃.这意味着在运⾏时⽆法获取到该注解的信息,只能在编译时使⽤

使用@annotation表达式

使用@annotation表达式定义切点时,只对@MyAspect生效

@RestController
@RequestMapping("/test")
public class TestController {@MyAspect@RequestMapping("/t1")public String t1(){return "t1";}}

在这里插入图片描述

完结撒花!🎉

如果这篇博客对你有帮助,不妨点个赞支持一下吧!👍
你的鼓励是我创作的最大动力~

想获取更多干货? 欢迎关注我的专栏 → optimistic_chen
📌 收藏本文,下次需要时不迷路!

我们下期再见!💫 持续更新中……


悄悄说:点击主页有更多精彩内容哦~ 😊

http://www.dtcms.com/a/594157.html

相关文章:

  • 4.95基于8086流水灯霓虹彩灯控制器,8086彩灯控制器proteus8.9仿真文件+源码功能四个开关对应四种模式。
  • 网站做百度推广需要什么材料专业的网站制作公司哪家好
  • 在 Ubuntu Desktop Linux 下解压7z文件的完整指南
  • 网站建设工作都包括哪些方面网站论文首页布局技巧
  • 国内做视频的网站网站优化需要做什么
  • 用 LangGraph + MCP Server 打造 SpreadJS 智能助手:让 AI 真正懂你的表格需求
  • 做网站用php还是node外贸网站 备案
  • 行业门户网站源码列举五种网络营销模式
  • 摄影建设网站wordpress插件装多了卡
  • 画世界Pro笔刷大全!含导入教程与多风格笔刷合集
  • 彩笔运维勇闯机器学习--多元线性回归(实战)
  • 免费推广店铺的网站网站默认首页怎么做
  • leetcode1377.T秒后青蛙的位置
  • 基于Yolo的图像识别中的特征融合
  • C语言自定义数据类型详解
  • 社交网站开发 转发建设网站的好处和优点
  • VBUS(Voltage Bus,电压总线) 是什么?
  • 前端做的网站潮阳网站制作
  • 北京哪家公司做网站好网站建设开发报价方案模板
  • 国家商标注册官网查询系统南京seo顾问
  • cpa单页网站怎么做sae wordpress 4.3
  • Vite与Webpack完全指南:从零开始理解前端构建工具
  • [优选算法专题六.模拟 ——NO.37~39 替换所有的问号、提莫攻击、Z 字形变换]
  • 【计算思维】蓝桥杯STEMA 科技素养考试真题及解析 4
  • HOT100题打卡第34天——二分查找
  • 电子商务网站的作用单位网站建设情况
  • 公司如何申请一个网站网址建设网站重庆
  • RedisTemplate 实战:Spring 项目中 Redis 操作的全维度指南
  • Doris Docker 完整部署指南
  • C语言算法:排序算法入门