Spring AOP -- 面相切面编程

AOP是Spring框架的核心之一,AOP是一种思想,它的实现方法有很多,有Spring AOP,也有AspectJ、CGLIB等。我们熟知的拦截器其实就是AOP思想的一种实现方式。

AOP是一种思想,是对某一类事情的集中处理。

Spring AOP的实现方式:

  1. 基于注解 @Aspect;
  2. 基于自定义注解;
  3. 基于Spring API(通过xml配置的方式);
  4. 基于代理来实现。

想要实现Spring Aop需要先引入以下依赖。

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

例如:我们此时想要优化一个接口的执行效率

此时有一个接口如下:

@RequestMapping("/aop")
@RestController
public class Main {@Autowiredprivate ForService fs;@RequestMapping("/fun1")public void fun1() {fs.fun(3);for (int i = 0; i < 1000; i++) {};}
}
@Service
public class ForService {public int fun(int i) {for (int j = 0; j < 3000; j++) {i++;}return i;}
}

我们首先需要知道这个接口在执行过程中调用的各个方法的执行时间,然后再对每个方法进行针对性优化。用AOP思想来实现:

基于注解 @Aspect

@Slf4j
@Component
@Aspect
public class WritTime {@Around("execution(* com.example.Spring_demo.aop.*.*(..)))")public Object time(ProceedingJoinPoint pjp) throws Throwable {//记录开始时间long start = System.currentTimeMillis();//执行目标方法Object a = pjp.proceed();//打印方法执行时间。pjp.toShortString()方法会返回方法签名的简写log.info(pjp.toShortString()+":"+(System.currentTimeMillis()-start)+"ms");return a;}
}

切点:也称之为"切入点",提供⼀组规则(切点表达式)告诉程序对哪些方法来进行功能增强;

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

通知:就是具体要做的工作,指哪些重复的逻辑,也就是共性功能(最终体现为⼀个方法);

切面:切点+通知。

切面类:切面所在的类,一个切面类中可以包含多个切面。

切点表达式:

常见的切点表达式有两种表达方式:

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

访问修饰限定符和异常可以被省略。

* :匹配任意字符,只匹配一个元素(返回类型,包,类名,方法或者方法参数)

  • 包名使用 * 表示任意包(一层包使用一个*);
  • 类名使用 * 表示任意类;
  • 返回值使用 * 表示任意返回值类型;
  • 方法名使用 * 表示任意方法;
  • 参数使用 * 表示⼀个任意类型的参数。

.. :匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数

  • 使用 .. 配置包名,标识此包以及此包下的所有子包;
  • 可以使用 .. 配置参数,任意个任意类型的参数。

+:表示匹配当前类和其子类

通知类型:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前后都被执行;
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行;
  • @After:后置通知,此注解标注的通知方法在目标方法后被执行;
  • @AfterReturning:返回后通知;
  • @AfterThrowing:异常后通知。

多种通知类型的执行顺序

现有如下接口:

@Slf4j
@RequestMapping("/aop")
@RestController
public class Main {@RequestMapping("/fun1")public void fun1() {log.info("执行fun1方法");}
}

定义如下切面类,里面有多个切面。

@Slf4j
@Component
@Aspect
public class WritTime {@Around("execution(* com.example.Spring_demo.aop.*.*(..)))")public Object time(ProceedingJoinPoint pjp) throws Throwable {log.info("@Around 方法前");//执行目标方法Object a = pjp.proceed();log.info("@Around 方法后");return a;}@Before("execution(* com.example.Spring_demo.aop.*.*(..)))")public void before() {log.info("@Before 前置通知");}@After("execution(* com.example.Spring_demo.aop.*.*(..)))")public void After() {log.info("@After 后置通知");}@AfterReturning("execution(* com.example.Spring_demo.aop.*.*(..)))")public void AfterReturning() {log.info("@AfterReturning 返回后通知");}@AfterThrowing("execution(* com.example.Spring_demo.aop.*.*(..)))")public void AfterThrowing() {log.info("@AfterThrowing 异常后通知");}
}

如果发生异常:

异常是可以再切面中被捕获的,但需要通知类型为@Around

@Pointcut

 @Pointcut注解可以把公共的切点表达式提取出来,需要用到时引用该切入点表达式即可。

@Slf4j
@Component
@Aspect
public class WritTime {@Pointcut("execution(* com.example.Spring_demo.aop.*.*(..)))")public void pc(){}@Around("pc()")public Object time(ProceedingJoinPoint pjp) {log.info("@Around 方法前");//执行目标方法Object a = null;try {a = pjp.proceed();} catch (Throwable e) {}log.info("@Around 方法后");return a;}@Before("pc()")public void before() {log.info("@Before 前置通知");}@After("pc()")public void After() {log.info("@After 后置通知");}@AfterReturning("pc()")public void AfterReturning() {log.info("@AfterReturning 返回后通知");}@AfterThrowing("pc()")public void AfterThrowing() {log.info("@AfterThrowing 异常后通知");}
}

声明的切面表达式也可以再其他切面类中使用但需要提前声明

@Slf4j
@Component
@Aspect
public class WritTime1 {//()里引用的切面表达式前必须加上全限定类名@Before("com.example.Spring_demo.aop.WritTime.pc()")public void before() {log.info("WritTime1通知");}
}

 

多个切面类的执行顺序

@Slf4j
@RequestMapping("/aop")
@RestController
public class Main {@RequestMapping("/fun1")public void fun1() {log.info("执行fun1方法");}
}

定义以下三个切面类,每个类里面只有一个切面:

//第一个
@Slf4j
@Component
@Aspect
public class WritTime1 {@Before("execution(* com.example.Spring_demo.aop.*.*(..)))")public void before() {log.info("WritTime1通知");}
}
//第二个
@Slf4j
@Component
@Aspect
public class WritTime2 {@Before("execution(* com.example.Spring_demo.aop.*.*(..)))")public void before() {log.info("WritTime2通知");}
}
//第三个
@Slf4j
@Component
@Aspect
public class WritTime3 {@Before("execution(* com.example.Spring_demo.aop.*.*(..)))")public void before() {log.info("WritTime3通知");}
}

 

如果存在多个切面类执行顺序默认是按照类名进行排序。

@Order

当存在多个切面类时可以通过@Order注解来定义各个切面类的优先级。

对上述切面类进行细微修改加上注解

//第一个
@Slf4j
@Component
@Aspect
@Order(3)
public class WritTime1 {……
}
//第二个
@Slf4j
@Component
@Aspect
@Order(2)
public class WritTime2 {……
}
//第三个
@Slf4j
@Component
@Aspect
@Order(1)
public class WritTime3 {……
}

基于自定义注解

使用自定义注解来计算方法的执行时间。

声明一个自定义注解:

//表示该接口只能作用于方法
@Target({ElementType.METHOD})
//该接口的生命周期
@Retention(RetentionPolicy.RUNTIME)public @interface Time {
}

使用切面类实现该注解的功能:

@Slf4j
@Component
@Aspect
public class WritTime {//@annotation里面的值为要实现的目标注解的全限定类名@Around("@annotation(com.example.Spring_demo.aop.Time)")public Object time(ProceedingJoinPoint pjp) throws Throwable {//记录开始时间long start = System.currentTimeMillis();//执行目标方法Object a = pjp.proceed();//打印方法执行时间。pjp.toShortString()方法会返回方法签名的简写log.info(pjp.toShortString()+":"+(System.currentTimeMillis()-start)+"ms(注解实现)");return a;}
}

给待测方法添加注解 

@Slf4j
@RequestMapping("/aop")
@RestController
public class Main {@Autowiredprivate ForService forService;@Time@RequestMapping("/fun1")public void fun1() {forService.fun(1);for (int i = 0; i < 10; i++) {}}
}

给已有的注解进行功能的增强

还可以使用上面的方式给已有的注解进行功能的增强(给@RequestMapping注解添加可以打印接口执行时间的功能):

@Slf4j
@Component
@Aspect
public class WritTime {//@annotation里面的值为@RequestMapping注解的全限定类名   @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")public Object time(ProceedingJoinPoint pjp) throws Throwable {//记录开始时间long start = System.currentTimeMillis();//执行目标方法Object a = pjp.proceed();//打印方法执行时间。pjp.toShortString()方法会返回方法签名的简写log.info(pjp.toShortString()+":"+(System.currentTimeMillis()-start)+"ms(注解实现)");return a;}
}

 

AOP的优势:

  1. 代码无侵入:不修改原始的业务方法,就可以对原始的业务方法进行了功能的增强或者是功能的改变;
     
  2. 减少了重复代码;
  3. 提高开发效率;
  4. 维护方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2815178.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】MySQL复合查询--多表查询自连接子查询 - 副本

文章目录 1.基本查询回顾2.多表查询3.自连接4.子查询 4.1单行子查询4.2多行子查询4.3多列子查询4.4在from子句中使用子查询4.5合并查询 4.5.1 union4.5.2 union all 1.基本查询回顾 表的内容如下&#xff1a; mysql> select * from emp; ----------------------------…

IAudioManager.cpp源码解读

IAudioManager.cpp源码如下&#xff1a; 源码路径&#xff1a;https://cs.android.com/android/platform/superproject/main//main:frameworks/native/services/audiomanager/IAudioManager.cpp;drc84410fbd18148d422d3581201c67f1a72a6658c4;l147?hlzh-cn /** Copyright (C)…

JavaWeb——005 请求响应 分层解耦(Postman、三层架构、IOC、DI、注解)

SpringBootWeb请求响应 这里写目录标题 SpringBootWeb请求响应前言1. 请求1.1 Postman1.1.1 介绍1.1.2 安装 1.2 简单参数1.2.1 原始方式1.2.2 SpringBoot方式1.2.3 参数名不一致 1.3 实体参数1.3.1 简单实体对象1.3.2 复杂实体对象 1.4 数组集合参数1.4.1 数组1.4.2 集合 1.5 …

Linux线程(二)----- 线程控制

目录 前言 一、线程资源区 1.1 线程私有资源 1.2 线程共享资源 1.3 原生线程库 二、线程控制接口 2.1 线程创建 2.1.1 创建一批线程 2.2 线程等待 2.3 终止线程 2.4 线程实战 2.5 其他接口 2.5.1 关闭线程 2.5.2 获取线程ID 2.5.3 线程分离 三、深入理解线程 …

WinCC如何与三菱Q系列PLC进行以太网通讯

本文主要描述人机界面WinCC如何与三菱Q系列PLC进行以太网通讯&#xff0c;主要介绍了CPU自带以太网口和扩展以太网模块两种情况以及分别使用TCP、UDP两种协议进行通讯组态步骤及其注意事项。 一、 说明 WinCC从V7.0 SP2版本开始增加了三菱以太网驱动程序&#xff0c;支持和三…

IPD(集成产品开发)—核心思想

企业发展到一定阶段就会遇到管理瓶颈&#xff0c;IPD流程是一种高度结构化的产品开发流程&#xff0c;它集成了业界很多优秀的产品开发方法论&#xff0c;像搭积木一样的组合成一种非常有效的流程。如果我们能根据企业的规模和行业特点&#xff0c;对全流程的IPD进行合适的裁剪…

数字乡村建设全攻略:从0到1的构建思路与实践

数字乡村建设是推进乡村振兴战略、实现农业农村现代化的重要抓手&#xff0c;其目标是通过数字化手段提升乡村治理效能&#xff0c;优化农村公共服务&#xff0c;推动农业产业升级&#xff0c;助力农民增收致富。 以下是从0到1构建数字乡村的总体思路与实践步骤&#xff1a;一、…

Day03:Web架构OSS存储负载均衡CDN加速反向代理WAF防护

目录 WAF CDN OSS 反向代理 负载均衡 思维导图 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&#xff1a;文件上传下载/端口服务/Shell反弹等 抓包技术&#xff1a…

STM32实现webserver显示数据及配置参数

之前已经在STM32中移植好了FREERTOSLWIP&#xff0c;要实现webserver配置参数及显示数据&#xff0c;需要使用到httpdcgissi cubeMx中配置以及代码实现参考&#xff1a;ECE471/571 (RTOS) STM32 FreeRTOSLwIP Example - Interactive Web Site 其实提到的将fsdata.c重命名为fs…

【视频编码\VVC】帧间预测编码基础知识

帧间预测编码概述 基本原理 利用时间相关性&#xff0c;使用邻近已编码图像像素值预测当前图像的像素值&#xff0c;能够有效去除时域冗余。目前的视频编码标准中&#xff0c;帧间预测都采用了基于块的运动补偿技术。 运动估计&#xff08;ME&#xff09;&#xff1a;当前图…

Dockerfile(2) - LABEL 指令详解

LABEL 可以为生成的镜像添加元数据标签信息&#xff0c;这些信息可以用来辅助过滤出特定镜像 LABEL <key><value> <key><value> <key><value> ... 栗子一 # key 加了 " LABEL "com.example.vendor""ACME Incorpor…

括号生成(力扣题目22)

题目描述&#xff1a; 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()&q…

vue组件中data为什么必须是一个函数

查看本专栏目录 关于作者 还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#x…

LeetCode19. 删除链表的倒数第 N 个结点(C++)

LeetCode19. 删除链表的倒数第 N 个结点 题目链接代码 题目链接 https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/ 代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : …

2024年sCrypt编程马拉松即将开幕

BSV区块链的建设者们&#xff0c;你们在哪&#xff1f;2024年sCrypt编程马拉松即将拉开帷幕&#xff01; 2024年3月16日至17日&#xff0c;我们将在旧金山市举办一场以比特币智能合约&#xff08;即 sCrypt&#xff09;和比特币通证&#xff08;如Ordinals&#xff09;相结合为…

Redisson 3.18.0版本解决failover相关问题

前言 Redisson 在历史多个版本都出现了failover期间报错的问题并且目前没有一个版本可以完全解决这个问题&#xff0c;所以在当前使用版本3.18.0基础上做了二次开发&#xff0c;达到降低业务由于redis遇到问题导致不可用。 背景 Redisson 作为业务线使用的Redis 客户端&…

unity学习(41)——创建(create)角色脚本(panel)——UserHandler(收)+CreateClick(发)——创建发包!

1.客户端的程序结构被我精简过&#xff0c;现在去MessageManager.cs中增加一个UserHandler函数&#xff0c;根据收到的包做对应的GameInfo赋值。 2.在Model文件夹下新增一个协议文件UserProtocol&#xff0c;内容很简单。 using System;public class UserProtocol {public co…

使用Rust加速Python程序,让代码飞起来

大家好&#xff0c;作为一种解释型语言&#xff0c;Python在开发速度和灵活性方面具有明显的优势&#xff0c;但在性能方面却不如编译型语言如C或Rust。对于性能要求苛刻的应用程序&#xff0c;如果纯粹使用Python编写可能会运行缓慢&#xff0c;影响用户体验。因此&#xff0c…

揭秘:MyBatis初恋的甜蜜!

&#x1f496;MyBatis的爱情故事&#x1f496; &#x1f339; 第一次遇见官方文档概述为什么需要MyBatis基本介绍MyBatis工作原理学习主线 &#x1f388; 第一次约会需求说明代码实现日志输出-查看SQL课后练习 &#x1f48c; 我们的情书MyBatis整体架构分析搭建MyBatis底层机制…

自由生成各种尺寸大小的占位图片:解决设计与开发中的图片占位问题

title: 自由生成各种尺寸大小的占位图片&#xff1a;解决设计与开发中的图片占位问题 date: 2024/2/28 20:29:11 updated: 2024/2/28 20:29:11 tags: 占位图片尺寸自定义设计效率用户体验响应式设计开发工具测试辅助 在设计与开发的过程中&#xff0c;经常需要使用占位图片来填…