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

Spring3之基于Aspect实现AOP

简介

使用 Aspect 搭配 Spring 可轻松实现 AOP;本章将通过一个完整示例演示如何实现这一功能

实现步骤

  1. 修改 beans.xml 配置文件的 schema 部分;可以在 spring-framework-reference.html 文件通过搜索关键字 “/aop” 找到配置 schema,然后把这三句 schema 添加到 beans.xml 文件中
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- 设置Spring去哪些包中找annotation --><context:component-scan base-package="com.ibm.oneteam"/>
</beans>
  1. 在 beans.xml 文件中添加如下基本配置
<context:component-scan base-package="com.duzimei.blog"/> <!-- 设置要被扫描的基础包 -->
<aop:aspectj-autoproxy/> <!-- 打开基于 Annotation 的AOP 设置 -->
  1. 添加如下 3 个依赖包到项目
    在这里插入图片描述

  2. 新建一个类 LogAspect.class, 添加如下代码


 // 声明这是一个切面类
public class LogAspect {// 在函数调用前执行("execution(* com.duzimei.blog.dao.*.add(..) || "+ "execution(* com.duzimei.blog.controller.*.delete(..))")public void logBefore(JoinPoint jp) {System.out.println("开始添加日志");// 输出执行对象System.out.println(jp.getTarget());// 输出执行方法System.out.println(jp.getSignature().getName());}// 在函数调用过程中执行("execution(* com.duzimei.blog.dao.*.add(..)) || "+ "execution(* com.duzimei.blog.controller.*.delete(..))")public Object logAround(ProceedingJoinPoint pjp) throws Throwable {System.out.print("1.开始添加日志");Object result = pjp.proceed();System.out.println("2.完成添加日志");return result;}// 在函数调用后执行("execution(* com.duzimei.blog.*.add(..)) || "+ "execution(* com.duzimei.blog.*.controller.delete(..))")public void logAfter() {System.out.println(“完成添加日志”);}// 函数调用出现异常(throwing="e", pointcut="execution(* com.duzimei.blog.*.add(..)) || "+ "execution(* com.duzimei.blog.controller.*.delete(..))")public void logError(Throwable e) {System.out.println("添加日志失败, " + e.getMessage());}// 函数调用完毕后处理返回值(pointcut="execution(* com.duzimei.blog.*.add(..)) || "+ "execution(* com.duzimei.blog.controller.*.delete(..))")public Object afterReturn() {System.out.println("获取到函数返回值");return result;}
}
  1. 测试一下;注意查看如下打印结果的执行顺序
    在这里插入图片描述

重要属性讲解

(1) @Before:在函数调用前执行

(2) @Around:在函数调用过程中执行,添加了该注解的方法是最主要的方法,基本上所有的逻辑都应在这个这个方法里

(3) @After:在函数执行完毕后执行

(4) @AfterThrowing:函数执行过程中出现异常;不常用,一般在 @Around 注解的方法中用 try … catch 处理较好

(5) @AfterReturning:函数执行完毕后执行,可获取、操作函数的返回值

(6) @Pointcut:用于定义切面,方便其他注解配置;如上面的例子中,每个方法的注解上都写了 execution 表达式,在实际开发过程中,可通过@Poingcut 定义不同的切面,然后在注解中直接调用,可简化代码;例如:

public class Pointcuts {// 定义只处理 dao 层 add 方法的切面配置("execution(* com.duzimei.blog.dao.*.add(..))")public void logAdd() { }// 定义值处理 controller 层 delete 方法的切面配置("execution(* com.duzimei.blog.controller.*.delete(..))")public void logDelete() { }
}

然后在 LogAspect.class 中可以这样使用



public class LogAspect {   ("com.duzimei.blog.aop.Pointcuts.logAdd()") public void logBefore() {... ...}
}

(7) execution 表达式:execution(* com.duzimei.blog.*.add(…)) 这个表达式中,第一个 * 表示任意返回值,第二个 * 表示 com.duzimei.blog 包路径下的所有类,第三个 * 表示以 add 开头的所有方法,(…) 表示方法的任意参数;多个表达式通过 || 分隔开

在 XML 文件中配置

上面演示的是通过直接在类中添加注解的方式实现 AOP,如果要使用 XML 的方式配置,只需添加如下代码即可

<bean id="logAspect" class="com.duzimei.blog.aop.LogAspect"/> <!-- 注入日志工具类 -->
<aop:config><!-- 定义切面 --><aop:aspect id="logAspect" ref="logAspect"><!-- 定义表达式和步骤 --><aop:pointcut id="logPointcut" expression="execution(* com.duzimei.blog.dao.*.add(..))"/><aop:before method="logStart" point-ref="logPointcut"/><aop:after method="logAfter" point-ref="logPointcut"/></aop:aspect>
</aop:config>

补充
在实际开发过程中,经常需要通过 AOP 实现日志记录功能,这里面稍微麻烦的地方在于如何获取方法的参数以及对应的值,因为我们在拦截的时候是不知道方法参数的类型的;这里有个简单的实现方式,参考代码如下

("execution(* com.dufu.blog.controller.*.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {// 获取方法参数Object[] args = point.getArgs();// 获取方法签名MethodSignature signature = (MethodSignature)point.getSignature();// 获取执行对象Object target = point.getTarget();// 获取方法Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());// 如果方法上添加了 @Logger 注解, 记录用户操作if(method.isAnnotationPresent(Logger.class)) {LocalVariableTableParameterNameDiscoverer parameter = new LocalVariableTableParameterNameDiscoverer();// 获取方法参数名称String[] paramNames = parameter.getParameterNames(method);// 通过 Map 记录参数Map<String, Object> paramMap = new HashMap<>();if(null != paramNames && paramNames.length > 0) {for(Integer i = 0; i < paramNames.length; i++) {// 忽略类型是 request, response, multipartFile 的参数if(args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof MultipartFile) {continue;}paramMap.put(paramNames[i], args[i]);}}// 打印一下参数信息System.out.println(JSON.toJSONString(paramMap));}return point.proceed();
}

相关文章:

Spring3之基于Aspect实现AOP

简介 使用 Aspect 搭配 Spring 可轻松实现 AOP&#xff1b;本章将通过一个完整示例演示如何实现这一功能 实现步骤 修改 beans.xml 配置文件的 schema 部分&#xff1b;可以在 spring-framework-reference.html 文件通过搜索关键字 “/aop” 找到配置 schema&#xff0c;然后…...

buctoj-寒假集训进阶训练赛(二十二)

问题 A: Stones 题目描述 由于自行车状态错误&#xff0c;森普尔开始每天早上从东到西走&#xff0c;每天晚上走回去。走路可能会有点累&#xff0c;所以森普这次总是玩一些游戏。 路上有很多石头&#xff0c;当他遇到一块石头时&#xff0c;如果是他遇到的奇数石头&#xff0…...

华为OD机试真题JAVA实现【静态扫描最优成本】真题+解题思路+代码(20222023)

🔥系列专栏 华为OD机试(JAVA)真题目录汇总华为OD机试(Python)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出描述示例一输入输出说明示例二输入输出说明...

汽车装配工厂立库物料运送线PLC无线应用

一、应用背景此次项目地在比亚迪的西安工厂&#xff0c;需要实现PLC无线通讯的地方是汽车厂的立体仓库物料运输线。生产物流担负运输、存储、装卸物料等任务。汽车制造业是典型的多工种、多工艺、多物料的大规模生产过程&#xff0c;因此原材料与零部件必需及时准确送至工位&am…...

Python雪花代码

前言 用python画个雪花玩玩&#xff0c;源码在文末公众号哈。 雪花类 class Snow(): #雪花类 def __init__(self): self.r 6 #雪花的半径 self.x ra.randint(-1000,1000) #雪花的横坐标 self.y ra.randint(-500,5…...

Numpy基础与实例——人工智能基础

文章目录一、Numpy概述1. 优势2. numpy历史3. Numpy的核心&#xff1a;多维数组4. 内存中的ndarray对象4.1 元数据&#xff08;metadata&#xff09;4.2 实际数据二、numpy基础1. ndarray数组2. arange、zeros、ones、zeros_like3. ndarray对象属性的基本操作3.1 修改数组维度3…...

MQTT的工作原理

介绍MQTT协议的消息模型,消息传输过程,消息发布和订阅。 一、介绍MQTT协议的消息模型 MQTT协议的消息模型被称为“主题”模型。在这种模型中,服务器接收到的消息将通过主题进行分类。客户端可以通过订阅一个或多个主题来接收所需的消息。 1.1 消息主题 1.2 消息内容 1.…...

iOS开发:UINavigationController自定义返回按钮,系统导航支持侧滑返回

当你使用系统导航想拦截用户返回事件时,无法拦截侧滑返回 当你自定义导航或者隐藏导航后,iOS系统导航的侧滑返回就失效了,那么用户体验将大打折扣 网上大部分自定义导航的解决方案是:给页面添加全局的轻扫手势,那么又区别于原生系统,改变了用户的操作习惯 在开发过程中,…...

【Kafka进阶】-- unclean.leader.election.enable参数的内涵

一、背景近期&#xff0c;我们的kafka 消息队列集群(1.x版本)经过了一次事故。某节点意外宕机&#xff0c;导致 log 文件损坏&#xff0c;重启 kafka 失败&#xff0c;最后导致某个 topic 的分区不可用&#xff0c;本文对此做了简单的分析、解决和复现参考&#xff0c;以此为记…...

基于redis实现分布式锁

前言 我们的系统都是分布式部署的&#xff0c;日常开发中&#xff0c;秒杀下单、抢购商品等等业务场景&#xff0c;为了防⽌库存超卖&#xff0c;都需要用到分布式锁。 分布式锁其实就是&#xff0c;控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或…...

C#开发的OpenRA动态加载插件DLL里的类实现

C#开发的OpenRA动态加载插件DLL里的类实现 由于这款游戏的设计是为了开源设计, 并且可以让不同个人或团体实现自己的游戏, 那么每个人实现的代码是不一样的,算法也是不一样的。 并且可能也拿不到代码一起编译生成一套运行的代码。 这时候,就要考虑使用动态加载类的功能。 意…...

网站代理是什么?有什么需要注意的?

如今&#xff0c;网站代理已经成为一种不可或缺的经营方式。无论是企业还是个人&#xff0c;都需要通过代理来获得更多的流量和市场份额。 一、网站代理的优势 网站代理的优势在于能够为您提供更加专业、周到的服务。这些优势包括&#xff1a;1.丰富的内容资源&#xff0c;能…...

动态库和静态库的区别

什么是库文件 一般来说&#xff0c;一个程序&#xff0c;通常都会包含目标文件和若干个库文件。经过汇编得到的目标文件再经过和库文件的链接&#xff0c;就能构成可执行文件。库文件像是一个代码仓库或代码组件的集合&#xff0c;为目标文件提供可直接使用的变量、函数、类等…...

C/C++路径去除前缀

在做一些日志输出的工作时&#xff0c;想要获取当前文件名&#xff0c;而不是冗长的文件路径。路径获取往往和各家os底层函数优化。C/C标准中定义了一些预处理宏&#xff0c;可以帮助我们获取文件路径。我们希望能够在编译期而不是在运行期做这个事情&#xff0c;避免额外的性能…...

Vue2之Vue-cli应用及组件基础认识

Vue2之Vue-cli应用及组件基础认识一、Vue-cli1、单页面应用程序2、vue-cli介绍3、安装和使用4、创建项目4.1 输入创建项目4.2 选择第三项&#xff0c;进行自主配置&#xff0c;按回车键即可4.3 选择自己需要的库4.4 选择Vue的版本4.5 选择CSS选择器4.6 选择Babel、ESLint、etc等…...

C 学习笔记 —— 声明、定义、初始化

文章目录声明定义初始化定义和初始化的区别静态变量初始化自动变量初始化声明 说明符表达式列表 int a; char j, k l;定义 一般的情况下&#xff0c;我们把建立空间的声明称之为定义&#xff0c;而把不需要建立存储空间的声明称之为声明。 int tern 1; //定义int main() {…...

机械狗控制算法

一. MIT Cheetah特点 1.驱动器 Cheetah 2采用了定制的本体感受驱动器设计&#xff0c;具有高冲击缓解、力控制和位置控制能力。这种设计使其能够自主跳过障碍物&#xff0c;并以6m/s的高速跳跃&#xff0c;但其运动范围有限&#xff0c;只能进行矢状面运动。 Cheetah 3采用高扭…...

向量与矩阵 导数和偏导数 特征值与特征向量 概率分布 期望方差 相关系数

文章目录向量与矩阵标量、向量、矩阵、张量向量范数和矩阵的范数导数和偏导数特征值和特征向量概率分布伯努利分布正态分布&#xff08;高斯分布&#xff09;指数分布期望、⽅差、协⽅差、相关系数期望方差协⽅差相关系数向量与矩阵 标量、向量、矩阵、张量 标量&#xff08;…...

记录--前端实现登录拼图验证

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 不知各位朋友现在在web端进行登录的时候有没有注意一个变化&#xff0c;以前登录的时候是直接账号密码通过就可以直接登录&#xff0c;再后来图形验证码&#xff0c;数字结果运算验证&#xff0c…...

【Go语言基础】Go语言中的map集合详细使用(附带源码)

文章目录Go语言中的map集合1-1 定义1-2 map遍历1-3 map集合删除1-4 map是引用类型Go语言中的map集合 Go 语言提供了内置类型 map集合&#xff0c;它将一个值与一个键关联起来&#xff0c;可以使用相应的键检索值。 map是一种集合&#xff0c;可以像遍历数组或切片那样去遍历它…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...