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下的…...

Java的编程之旅24——private私有方法
1.private的介绍 在面向对象编程中,private是一种访问修饰符,用于限制成员的访问范围。私有成员只能在所属的类内部访问,对外部的类或对象是不可见的。 private的使用可以带来以下几个好处: 封装实现细节:私有成员可…...

为什么在MOS管开关电路设计中使用三极管容易烧坏?
MOS管作为一种常用的开关元件,具有低导通电阻、高开关速度和低功耗等优点,因此在许多电子设备中广泛应用。然而,在一些特殊情况下,我们需要在MOS管控制电路中加入三极管来实现一些特殊功能。然而,不同于MOS管ÿ…...

CSS的注释:以“ /* ”开头,以“ */ ”结尾
CSS的注释:以“ /* ”开头,以“*/”结尾 CSS的注释: 以“ /* ”开头,以“ */ ”结尾 在CSS中,注释是一种非常重要的工具,它们可以帮助开发者记录代码的功能、用法或其他重要信息。这些信息对于理解代码、维护代码以及与他人合作都…...

MySQL中常见的几种日志类型【重点】
在MySQL中,有几种不同类型的日志,用于记录数据库的活动和操作,以便于故障排查、性能调优和数据恢复等目的。以下是MySQL中常见的几种日志类型: 错误日志(Error Log): 错误日志记录了MySQL服务器…...

odoo16-API(Controller)带有验证访问的接口
odoo16-API(Controller)带有验证访问的接口 目前我使用odoo原生的登录token来验证登陆的有效性 废话不多说直接上代码 # 测试获取session_id import requests class GetOdooData(http.Controller):def getOdooToken(self):# http://localhost:8123访问…...

Eclipse项目间的引用
我们在开发的时候,有时候需要把一个大的项目打散,尤其是现在微服务的架构很流行,一个大的项目往往被拆成很多小的项目,而有的项目作为公共工程被独立出来,比如有个工程专门提供各种Util工具类,有的工程专门…...

matplotlib使用案例3:通过自定义图例类实现图例的任意方向(行 or 列)的排列
这个方法的核心依然是基于matplotlib.legend._get_legend_handles_labels函数。然后将得到的handlers, labels进行重排,使得即使再调用Legend类的绘制方法对图例进行列排列,最终的效果也是图例的行显示,如[1、2、3、4、5、6],当指定ncols=2,Legend类的绘制方法得到的图例如…...

js设计模式:依赖注入模式
作用: 在对象外部完成两个对象的注入绑定等操作 这样可以将代码解耦,方便维护和扩展 vue中使用use注册其他插件就是在外部创建依赖关系的 示例: class App{constructor(appName,appFun){this.appName appNamethis.appFun appFun}}class Phone{constructor(app) {this.nam…...

【性能最佳实践】事务处理和读写策略原来这么关键!
MongoDB针对初级,中级及熟练的技术开发人员推出系列技术文章与行业案例。深入浅出地剖析MongoDB产品基础原理,使用技巧,典型行业场景及应用,还有Code Demo及线上线下活动推荐! 欢迎阅读有关MongoDB性能最佳实践的系列…...

【广度优先搜索】【网格】【割点】【 推荐】1263. 推箱子
作者推荐 视频算法专题 涉及知识点 广度优先搜索 网格 割点 并集查找 LeetCode:1263. 推箱子 「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。 游戏地图用大小为 m x n 的网格 grid 表示,其中每个元素可以是墙、地板或…...