Spring之AOP
文章目录
- 初步实现
- 通知执行顺序
- 各个通知获取细节信息
- 重用切点表达式
- 切点表达式语法细节
- 环绕增强
- 切面的优先级
- 没有接口的情况
- 基于XML的AOP[了解]
初步实现
先导入Spring和Junit4的依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.11.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.11.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
导入AOP的依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.11.RELEASE</version></dependency>
在Spring的配置文件中开启包扫描和AOP注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="com.iflytek"></context:component-scan><!-- 开启基于注解的AOP功能 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
也可以通过配置类开启
@Configuration
@ComponentScan(basePackages = "com.iflytek")
@EnableAspectJAutoProxy
public class MyConfig {
}
创建接口
public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);}
创建实现类实现这个接口:
@Component
public class CalculatorPureImpl implements Calculator{@Overridepublic int add(int i, int j) {int result = i + j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int sub(int i, int j) {int result = i - j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int mul(int i, int j) {int result = i * j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int div(int i, int j) {int result = i / j;System.out.println("方法内部 result = " + result);return result;}
}
定义切面类
/*** 切面类*/
// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {// @Before注解:声明当前方法是前置通知方法// value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上@Before(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void printLogBeforeCore() {System.out.println("[AOP前置通知] 方法开始了");}@AfterReturning(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void printLogAfterSuccess() {System.out.println("[AOP返回通知] 方法成功返回了");}@AfterThrowing(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void printLogAfterException() {System.out.println("[AOP异常通知] 方法抛异常了");}@After(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void printLogFinallyEnd() {System.out.println("[AOP后置通知] 方法最终结束了");}}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class testClass {@Autowiredprivate Calculator calculator;//这里一定要导入接口,而非实现类@Testpublic void test01(){calculator.add(1,2);}
}
运行结果:
[AOP前置通知] 方法开始了
方法内部 result = 3
[AOP返回通知] 方法成功返回了
[AOP后置通知] 方法最终结束了
通知执行顺序
- Spring版本5.3.x以前:
- 前置通知(@Before)
- 目标操作
- 后置通知(@After)
- 返回通知或异常通知(@AfterReturing/@AfterThrowing)
- Spring版本5.3.x以后:
- 前置通知(@Before)
- 目标操作
- 返回通知或异常通知(@AfterReturing/@AfterThrowing)
- 后置通知(@After)
各个通知获取细节信息
JoinPoint接口
org.aspectj.lang.JoinPoint
- 要点1:JoinPoint接口通过getSignature()方法获取目标方法的签名
- 要点2:通过目标方法签名对象获取方法名
- 要点3:通过JoinPoint对象获取外界调用目标方法时传入的实参列表组成的数组
在切面类中:
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {// @Before注解:声明当前方法是前置通知方法// value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上@Before(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void printLogBeforeCore(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();//方法名addSystem.out.println("methodName:"+signature.getName());//获取目标方法声明类型(public、private、protected) 1025System.out.println("modifiers:"+signature.getModifiers());// 获取目标方法所属类的类名 com.iflytek.CalculatorSystem.out.println("declaringTypeName:"+signature.getDeclaringTypeName());Object[] args = joinPoint.getArgs();List<Object> list = Arrays.asList(args);System.out.println("参数:");//1,2list.forEach(item->{System.out.println(item);});System.out.println("[AOP前置通知] 方法开始了");}@AfterReturning(value = "execution(public int com.iflytek.Calculator.add(int,int))",returning = "result")//returning 获取方法返回值public void printLogAfterSuccess(Integer result) {System.out.println("[AOP返回通知] 方法成功返回了,返回值为"+result);}@AfterThrowing(value = "execution(public int com.iflytek.Calculator.add(int,int))",throwing = "throwable")//throwing 获取异常信息public void printLogAfterException(Throwable throwable) {System.out.println("[AOP异常通知] 方法抛异常了"+throwable.getClass().getName());}@After(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void printLogFinallyEnd() {System.out.println("[AOP后置通知] 方法最终结束了");}}
重用切点表达式
1、在切面类中声明
//定义切点@Pointcut(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void pointCut(){}
2、同一个类内部引用
@Before(value = "pointCut()")public void printLogBeforeCoreOperation(JoinPoint joinPoint) {
3、在不同类中引用
该方法在LogAspect2类中
@Before(value = "com.iflytek.LogAspect.pointCut()")public Object roundAdvice(ProceedingJoinPoint joinPoint) {}
4、集中管理
而作为存放切入点表达式的类,可以把整个项目中所有切入点表达式全部集中过来,便于统一管理:
@Component
public class PointCuts {@Pointcut(value = "execution(public int *..Calculator.sub(int,int))")public void globalPointCut(){}@Pointcut(value = "execution(public int *..Calculator.add(int,int))")public void secondPointCut(){}@Pointcut(value = "execution(* *..*Service.*(..))")public void transactionPointCut(){}
}
切点表达式语法细节
- 用开头的*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
- 在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
- 例如:*.Hello匹配com.Hello,不匹配com.iflytek.Hello
- 在包名的部分,使用“*…”表示包名任意、包的层次深度任意
- 在类名的部分,类名部分整体用*号代替,表示类名任意
- 在类名的部分,可以使用*号代替类名的一部分
*Service
上面例子表示匹配所有名称以Service结尾的类或接口
- 在方法名部分,可以使用*号表示方法名任意
- 在方法名部分,可以使用*号代替方法名的一部分
*Operation
上面例子表示匹配所有方法名以Operation结尾的方法:
- 在方法参数列表部分,使用(…)表示参数列表任意
- 在方法参数列表部分,使用(int,…)表示参数列表以一个int类型的参数开头
- 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的
- 切入点表达式中使用 int 和实际方法中 Integer 是不匹配的
- 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
execution(public int *..*Service.*(.., int))
上面例子是对的,下面例子是错的:
execution(* int *..*Service.*(.., int))
但是public *表示权限修饰符明确,返回值任意是可以的。
* void 就是错误的
- 对于execution()表达式整体可以使用三个逻辑运算符号
- execution() || execution()表示满足两个execution()中的任何一个即可
- execution() && execution()表示两个execution()表达式必须都满足
- !execution()表示不满足表达式的其他方法
环绕增强
环绕通知对应整个try…catch…finally结构,包括前面四种通知的所有功能。
//定义切点@Pointcut(value = "execution(public int com.iflytek.Calculator.add(int,int))")public void pointCut(){}@Around(value = "pointCut()")public void printAround(ProceedingJoinPoint joinPoint){try {//此处相当于前置增强System.out.println("before...");joinPoint.proceed(joinPoint.getArgs());System.out.println("afterReturning...");} catch (Throwable e) {
// throw new RuntimeException(e);e.printStackTrace();System.out.println("afterThrowing");}finally {//此处相当于后置增强System.out.println("after...");}}
切面的优先级
相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。
- 优先级高的切面:外面
- 优先级低的切面:里面
eg:如果 是@Before前置增强,则优先级高的先执行
如果是@After后置增强,则优先级低的先执行
使用@Order注解可以控制切面的优先级:
- @Order(较小的数):优先级高
- @Order(较大的数):优先级低
没有接口的情况
在目标类没有实现任何接口的情况下,Spring会自动使用cglib技术实现代理
基于XML的AOP[了解]
将之前用到的AOP的注解都删除
使用xml配置
<bean id="aspect" class="com.iflytek.LogAspect"></bean><bean id="calculatorPure" class="com.iflytek.CalculatorPureImpl"></bean><aop:config>
<!-- 定义切入点和规则--><aop:pointcut id="pc" expression="execution(public Integer com.iflytek.Calculator.add(int ,int))"/>
<!--切面--><aop:aspect ref="aspect"><aop:before method="printLogBeforeCore" pointcut-ref="pc"></aop:before><aop:after-returning method="printLogAfterSuccess" pointcut-ref="pc" returning="result"></aop:after-returning><aop:after-throwing method="printLogAfterException" pointcut-ref="pc" throwing="throwable"></aop:after-throwing><aop:after method="printLogFinallyEnd" pointcut-ref="pc"></aop:after></aop:aspect></aop:config>
相关文章:

Spring之AOP
文章目录 初步实现通知执行顺序 各个通知获取细节信息重用切点表达式切点表达式语法细节环绕增强切面的优先级没有接口的情况基于XML的AOP[了解] 初步实现 先导入Spring和Junit4的依赖 <dependency><groupId>org.springframework</groupId><artifactId&g…...

Git详解及 github与gitlab使用
目录 1.1 关于版本控制 1.1.1 本地版本控制 1.1.2 集中化的版本控制系统 1.1.3 分布式版本控制系统 1.2 Git简介 1.2.1 Git历史 1.3 安装git 1.3.1 环境说明 1.3.2 Yum安装Git 1.3.3 编译安装 1.4 初次运行 Git 前的配置 1.4.1 配置git 1.4.2 获取帮助 1.5 获取 G…...
政安晨:【完全零基础】认知人工智能(二)【超级简单】的【机器学习神经网络】—— 底层算法
如果小伙伴第一次看到这篇文章,可以先浏览一下我这个系列的上一篇文章: 政安晨:【完全零基础】认知人工智能(一)【超级简单】的【机器学习神经网络】 —— 预测机https://blog.csdn.net/snowdenkeke/article/details/…...

基于springboot+vue的美发门店管理系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...

C语言第二十八弹---整数在内存中的存储
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 目录 1、整数在内存中的存储 2、大小端字节序和字节序 2.1、什么是大小端? 2.2、为什么有大小端? 2.3、练习 2.3.1、练习1 2.3.2、练习2 2.…...
java开源xml工具类介绍
在Java中处理XML的常用开源工具有很多,以下是一些流行的库以及简单的示例代码: DOM4J DOM4J 是一个非常流行的Java库,用于处理XML,DOM4J 易于使用,并且提供了很好的性能。 Maven 依赖 …...
Go 语言一些常用语法编写和优化指南
Go 语言以其简洁的语法和强大的并发性能而受到开发者的喜爱。然而,为了充分利用 Go 的潜力,我们需要了解如何优化 Go 程序。本文将介绍一些常见的 Go 语言优化技巧,并通过实际例子进行说明。 推荐系列 来来来,老铁们,男人女人都需要的技术活…...
Golang 语法系列:结构体
结构体:相当于"类" 1.结构体声明 type [name] struct {[field_name] [field_type][field_name] [field_type]... }//例子:type Person struct {name stringage int }其中field_name可以省略 2.结构体的使用 1) 格式1 var person Person p…...

关于iPad中的密码和触控ID的使用,看这篇文章就差不多了
序言 许多苹果iPad型号都有熟悉的密码系统和触控ID,这需要指纹扫描才能解锁设备。本指南向你展示如何使用iPad Air 2或更高版本、iPad Mini 3或更新版本以及iPad Pro设置或更改密码和触控ID指纹。 一些iPad Pro型号支持面部识别,并配备了面容ID而不是触控ID作为安全功能。面…...

Vue3之ref与reactive的基本使用
ref可以创建基本类型、对象类型的响应式数据 reactive只可以创建对象类型的响应式数据 接下来让我为大家介绍一下吧! 在Vue3中,我们想让数据变成响应式数据,我们需要借助到ref与reactive 先为大家介绍一下ref如何使用还有什么注意点 我们需…...
wsl内置Ubuntu使用 Dinky 与 Flink 集成
Dinky 与 Flink 集成 说明 本文档介绍 Dinky 与 Flink 集成的使用方法, 如果您是 Dinky 的新用户, 请先阅读 本文档, 以便更好的搭建 Dinky 环境 如果您已经熟悉 Dinky 并已经部署了 Dinky, 请跳过本文档的前置要求部分, 直接阅读 Dinky 与 Flink 集成部分 注意: 本文档基…...

”戏说“ 交换机 与 路由器
一般意义上说 老哥 这文章发表 的 东一榔头 西一锤 呵呵, 想到哪里就啰嗦到哪里 。 交换机: 其实就是在通道交换 路由器: 不光是在通道交换还要在协议上交换 下图你看懂了吗? (仅仅数据交换-交换机 协议…...
Linux pageset
1. 引言 在用户进程发生缺页异常时,Linux内核需要分配所需物理页面以及建立也表映射,来维持进程的正常内存使用需求。而对于分配物理页面仅依赖于buddy系统,对于小order页面的分配效率较低。因此Linux通过在每个cpu维护一个page链表ÿ…...
【C++之语法篇003】
C学习笔记---003 C知识开篇1、内联函数1.1、什么是内联函数?1.2、解决外部头文件,重复定义问题1.3、内联函数的总结 2、auto关键字2.1、auto的作用2.2、auto的总结 3、范围for3.1、什么是范围for?3.2、范围for的循环应用 4、指针空值关键字nullptr4.1、…...

Github代码仓库SSH配置流程
作者: Herman Ye Auromix 测试环境: Ubuntu20.04 更新日期: 2024/02/21 注1: Auromix 是一个机器人爱好者开源组织。 注2: 由于笔者水平有限,以下内容可能存在事实性错误。 相关背景 在为Github代码仓库配…...

Arrays工具类的常见方法总结
一、Arrays.asList( ) 1.作用:Arrays.asList( )方法的作用是将数组转换成List,将List中的全部集合对象添加至ArrayList集合中 2.参数:动态参数 (T... a) 3.返回值:List 集合 List<T> 4.举例: package com…...
物联网和人工智能的融合
物联网和人工智能的融合 1. 物联网和人工智能的融合2. 芯片技术的进步3. 安全和隐私保护挑战4. 软件开发和调试技术的创新5. 自动化和智能化趋势 1. 物联网和人工智能的融合 随着物联网和人工智能技术的快速发展,嵌入式系统将更多地与物联网设备和人工智能算法相结…...
【微信小程序】wxss 和 css 、wxml 和 html 区别
wxss 和 css 区别 wxss 支持小程序特有的选择器和 样式属性 scroll-into-view cover-view 等wxss 引入了 rpx 单位,可以根据屏幕宽度进行自适应,使得开发者可以更方便的处理不同尺寸屏幕的适配问题。wxss 背景图片只能引入外链,不能使用本地…...

python统计分析——使用AIC进行模型选择
参考资料:用python动手学统计学 1、导入库 # 导入库 # 用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 用于绘图的库 import matplotlib.pyplot as plt import seaborn as sns sns.set() # 用于估计统计…...

Android 11以上获取不到第三方app是否安装
开年第一篇,处理了一下年前的小问题。 问题:本地app跳转到第三方app地图进行导航,获取不到第三方地图是否安装。 解决: 1.添加包名 This can be done by adding a <queries> element in the Android manifest.在app下的…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...

数据挖掘是什么?数据挖掘技术有哪些?
目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...

C++ Saucer 编写Windows桌面应用
文章目录 一、背景二、Saucer 简介核心特性典型应用场景 三、生成自己的项目四、以Win32项目方式构建Win32项目禁用最大化按钮 五、总结 一、背景 使用Saucer框架,开发Windows桌面应用,把一个html页面作为GUI设计放到Saucer里,隐藏掉运行时弹…...

【前端实战】如何让用户回到上次阅读的位置?
目录 【前端实战】如何让用户回到上次阅读的位置? 一、总体思路 1、核心目标 2、涉及到的技术 二、实现方案详解 1、基础方法:监听滚动,记录 scrollTop(不推荐) 2、Intersection Observer 插入探针元素 3、基…...
2025年全国I卷数学压轴题解答
第19题第3问: b b b 使得存在 t t t, 对于任意的 x x x, 5 cos x − cos ( 5 x t ) < b 5\cos x-\cos(5xt)<b 5cosx−cos(5xt)<b, 求 b b b 的最小值. 解: b b b 的最小值 b m i n min t max x g ( x , t ) b_{min}\min_{t} \max_{x} g(x,t) bmi…...

【见合八方平面波导外腔激光器专题系列】用于干涉光纤传感的低噪声平面波导外腔激光器2
----翻译自Mazin Alalus等人的文章 摘要 1550 nm DWDM 平面波导外腔激光器具有低相位/频率噪声、窄线宽和低 RIN 等特点。该腔体包括一个半导体增益芯片和一个带布拉格光栅的平面光波电路波导,采用 14 引脚蝶形封装。这种平面波导外腔激光器设计用于在振动和恶劣的…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术点解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术点解析 第一轮:基础概念问题 请解释Spring框架的核心容器是什么?它的作用是什么? 程序员JY回答:Spring框架的核心容器是IoC容器(控制反转…...
十、【ESP32开发全栈指南: TCP客户端】
一、TCP协议核心特性回顾 TCP与UDP关键差异 特性TCPUDP连接方式面向连接 (三次握手)无连接可靠性可靠传输 (重传/排序/校验)尽力交付数据顺序保证数据按序到达不保证顺序流控制滑动窗口机制无流控制传输效率协议开销大头部开销小适用场景文件传输、网页浏览实时音视频、广播通…...