SSM学习——Spring AOP与AspectJ
Spring AOP与AspectJ
概念
AOP的全称为Aspect-Oriented Programming,即面向切面编程。
想象你是汉堡店的厨师,每一份汉堡都有好几层,这每一层都可以视作一个切面。现在有一位顾客想要品尝到不同风味肉馅的汉堡,如果按照传统的方式,你需要做多个汉堡,每个汉堡只有肉馅是不一样的,但是你每做一个汉堡都要重新制作面包。而聪明的厨师只需做一个汉堡,仅将肉饼那一层分成不同口味的几个区域,这样你就不需要再重复制作面包了。
对于程序员也是一样的,有多少个接口就要写或复制多少代码那一定是无法忍受的,我们只想关心不同的那部分。
尽管想通俗来讲,但是还是要去熟悉专业的概念:
Aspect:切面,类似于Java类声明,里面会有Pointcut和AdviceJoint point:连接点,在程序执行过程中某个阶段点Pointcut:切入点,切面与程序流的交叉点,往往此处需要处理Advice:通知或增强,在切入点处所要执行的代码。可以理解为切面类中的方法。Target object:目标对象,指所有被通知的对象。Proxy:代理,将通知应用到目标对象后,被动态创建的对象。Weaving:织入,将切面代码插到目标对象上,从而生成代理对象的过程。
别担心,我们之后会通过代码来慢慢理解。
AOP的实现
AOP的实现主要分为静态代理和动态代理,在本教程中静态代理我们用AspectJ,而动态代理用Spring AOP。
静态代理在编译期就确定了代理类,而动态代理需要靠反射机制动态生成代理类。
Spring AOP动态代理有两种实现方式:一种是JDK动态代理,这种方式需要接口;另一种是CGLib动态代理,这种方式不依赖接口。
有了以上的知识,我们开始写代码,首先新创建一个Maven项目top.cairbin.test2,如果你不会请回去看之前的章节。
然后在pom.xml的<dependencies></dependencies>之间添加依赖包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version>
</dependency>
我们去实现一个IUser接口,要求接口内有两个方法void addUser()和void deleteUser()
package top.cairbin.test2;public interface IUser {void addUser();void deleteUser();
}
接下来定义一个实现该接口的User类
package top.cairbin.test2;public class User implements IUser{@Overridepublic void addUser() {System.out.println("进行增加用户操作!");}@Overridepublic void deleteUser() {System.out.println("进行删除用户操作!");}
}
我们定义一个切面类,该类中的两个方法void check()和void log()分别模拟权限检查和日志记录功能。
切面类如下所示
package top.cairbin.test2;public class MyAspect {public void check() {System.out.println("正在模拟权限认证");}public void log() {System.out.println("正在模拟日志记录");}
}
JDK动态代理
接下来创建代理类JdkProxy,这个类实现了JDK动态代理的InvocationHandler接口,并实现代理方法。
package top.cairbin.test2;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkProxy implements InvocationHandler{private final IUser user;public JdkProxy(IUser user) {this.user = user;}public static Object createProxy(IUser user) {ClassLoader classLoader = JdkProxy.class.getClassLoader();// 被代理对象实现的所有接口Class[] clazz = user.getClass().getInterfaces();// 使用代理类,进行增强,返回的是代理后的对象return Proxy.newProxyInstance(classLoader,clazz,new JdkProxy(user));}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 声明切面MyAspect myAspect = new MyAspect();// 前增强myAspect.check();// 在目标类上调用方法,并传入参数Object obj = method.invoke(user, args);// 后增强myAspect.log();return obj;}}
然后尝试在App.java的main方法中使用它们
package top.cairbin.test2;public class App
{public static void main( String[] args ){// 创建目标对象IUser user= new User();// 创建代理,并从代理中获取增强后的目标对象IUser user2 = (IUser)JdkProxy.createProxy(user);// 执行方法user2.addUser();user2.deleteUser();}
}
输出结果如下图所示

CGLib动态代理
我们不妨尝试使用CGLib来玩一下
创建一个新的类,名称为CglibProxy,并实现接口MethodInterceptor以及相应的方法
package top.cairbin.test2;import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {public static Object createProxy(Object target){Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new CglibProxy()); return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 声明切面MyAspect myAspect = new MyAspect();// 前增强myAspect.check();//获取增强后的目标对象Object target = proxy.invokeSuper(obj, args);// 后增强myAspect.log();return target;}
}
尝试调用下
package top.cairbin.test2;public class App
{public static void main( String[] args ){IUser user = (IUser)CglibProxy.createProxy (new User());user.addUser();user.deleteUser();}
}
不出所料,果然成功了

我们仔细观察CGLib的这几段代码,在CglibProxy类中我们并没有用到IUser这个接口,而是返回Object,然后外面也就是调用者那里强制转换为IUser类型!
不妨再“懒”一些,我们借助Spring的依赖注入,从Spring的容器中直接返回增强后的实现了IUser接口的对象试一试。
首先在resources/AppCtx.xml中编写Bean的配置(这里有坑,如果行不通回前面的文章看看)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><!-- 目标类 --><bean id="user" class="top.cairbin.test2.User" /><!-- 切面类 --><bean id="myAspect" class="top.cairbin.test2.MyAspect" /><!-- 使用Spring代理工厂定义一个名称为userProxy的代理对象 --><bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定代理实现的接口--><property name="proxyInterfaces" value="top.cairbin.test2.IUser" /><!-- 指定目标对象 --><property name="target" ref="user" /><!-- 指定切面,织入环绕通知 --><property name="interceptorNames" value="myAspect" /><!-- 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 --><property name="proxyTargetClass" value="true" /></bean>
</beans>
修改下MyAspect类,并实现接口MethodInterceptor
package top.cairbin.test2;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;public class MyAspect implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation mi) throws Throwable {this.check();// 执行目标方法Object obj = mi.proceed();this.log();return obj;}public void check() {System.out.println("正在模拟权限认证");}public void log() {System.out.println("正在模拟日志记录");}
}
App类中的main方法调用如下
package top.cairbin.test2;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App
{public static void main( String[] args ){ApplicationContext app = new ClassPathXmlApplicationContext("AppCtx.xml");IUser user = (IUser)app.getBean("userProxy");user.addUser();user.deleteUser();}
}
点击运行得到结果

AspectJ静态代理
使用AspectJ静态代理,我们重新设计下MyAspect切面类
package top.cairbin.test2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/*** 切面类,在此类中编写通知*/
@Aspect
@Component
public class MyAspect {// 定义切入点表达式@Pointcut("execution(* top.cairbin.test2.*.*(..))")// 使用一个返回值为void、方法体为空的方法来命名切入点private void myPointCut(){}// 前置通知@Before("myPointCut()")public void myBefore(JoinPoint joinPoint) {System.out.print("前置通知 :模拟执行权限检查...,");System.out.print("目标类是:"+joinPoint.getTarget() );System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());}// 后置通知@AfterReturning(value="myPointCut()")public void myAfterReturning(JoinPoint joinPoint) {System.out.print("后置通知:模拟记录日志...," );System.out.println("被织入增强处理的目标方法为:"+ joinPoint.getSignature().getName());}// 环绕通知 @Around("myPointCut()")public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {// 开始System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");// 执行当前目标方法Object obj = proceedingJoinPoint.proceed();// 结束System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");return obj;}// 异常通知@AfterThrowing(value="myPointCut()",throwing="e")public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {System.out.println("异常通知:" + "出错了" + e.getMessage());}// 最终通知@After("myPointCut()")public void myAfter() {System.out.println("最终通知:模拟方法结束后的释放资源...");}
}
对于切入点注解@Pointcut("execution(* top.cairbin.test2.*.*(..))")表示对top.cairbin.test2这个包下的所有类的所有方法生效。
我们再来看看Spring中的Advice的几种类型:
org.springframework.aop.MethodBeforeAdvice,前置通知,目标方法执行前实施,可用于权限管理。org.springframework.aop.AfterReturningAdvice,后置通知,在目标方法执行后实施,用于关闭文件流、上传文件、删除临时文件等。org.aopalliance.intercept.MethodInterceptor,环绕通知,在目标方法实施前后,一般用于日志或事务管理。org.springframework.aop.ThrowsAdvice,异常抛出通知,在抛出异常后实施。org.springframework.aop.IntroductionInterceptor,引介通知,在目标类中添加新方法和属性,可以应用于修改老版本程序。
我们还要实现自动扫描和依赖注入,看看我们的User类
package top.cairbin.test2;
import org.springframework.stereotype.Component;@Component
public class User implements IUser{@Overridepublic void addUser() {System.out.println("进行增加用户操作!");}@Overridepublic void deleteUser() {System.out.println("进行删除用户操作!");}
}
自然也少不了AppCtx.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:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"><!-- 指定需要扫描的包,使注解生效 --><context:component-scan base-package="top.cairbin.test2" /><!-- 启动基于注解的声明式AspectJ支持 --><aop:aspectj-autoproxy />
</beans>
在main方法中测试下,为了清楚,我这里仅调用了addUser一个方法
package top.cairbin.test2;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App
{public static void main( String[] args ){ApplicationContext app = new ClassPathXmlApplicationContext("AppCtx.xml");IUser user = (IUser)app.getBean("user");user.addUser();}
}

我们发现当环绕通知与前置通知和后置通知同时使用的时候,优先级如下:
- 环绕通知开始
- 前置通知
- 方法执行
- 后置通知
- 环绕通知结束
想必到了这里,你对AspectJ的使用有了一定的了解,但是对于相应的注解还是不太清楚,请仔细阅读下方图片中的表格,结合一开始的术语体会下:

相关文章:
SSM学习——Spring AOP与AspectJ
Spring AOP与AspectJ 概念 AOP的全称为Aspect-Oriented Programming,即面向切面编程。 想象你是汉堡店的厨师,每一份汉堡都有好几层,这每一层都可以视作一个切面。现在有一位顾客想要品尝到不同风味肉馅的汉堡,如果按照传统的方…...
Android 使用LeakCanary检测内存泄漏,分析原因
内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。 平时我们在使用app时,少量的内存泄漏我们是发现不了的,但是当内存泄漏达到一定数量时&…...
Linux部署Kafka2.8.1
安装Jdk 首先确保你的机器上安装了Jdk,Kafka需要Java运行环境,低版本的Kafka还需要Zookeeper,我此次要安装的Kafka版本为2.8.1,已经内置了一个Zookeeper环境,所以我们可以不部署Zookeeper直接使用。 1、解压Jdk包 t…...
【pytest、playwright】allure报告生成视频和图片
目录 1、修改插件pytest_playwright 2、conftest.py配置 3、修改pytest.ini文件 4、运行case 5、注意事项 1、修改插件pytest_playwright pytest_playwright.py内容如下: # Copyright (c) Microsoft Corporation. # # Licensed under the Apache License, Ver…...
浅谈iOS开发中的自动引用计数ARC
1.ARC是什么 我们知道,在C语言中,创建对象时必须手动分配和释放适量的内存。然而,在 Swift 中,当不再需要类实例时,ARC 会自动释放这些实例的内存。 Swift 使用 ARC 来跟踪和管理应用程序的内存,其主要是由…...
Spring IoCDI(2)
IoC详解 通过上面的案例, 我们已经知道了IoC和DI的基本操作, 接下来我们来系统地学习Spring IoC和DI的操作. 前面我们提到的IoC控制反转, 就是将对象的控制权交给Spring的IoC容器, 由IoC容器创建及管理对象. (也就是Bean的存储). Bean的存储 我们之前只讲到了Component注解…...
30. UE5 RPG GamplayAbility的配置项
在上一篇文章,我们介绍了如何将GA应用到角色身上的,接下来这篇文章,将主要介绍一下GA的相关配置项。 在这之前,再多一嘴,你要能激活技能,首先要先应用到ASC上面,才能够被激活。 标签 之前介绍…...
提升自己最快的方式是什么?
提升自己最快的方式通常涉及到个人成长的各个方面,包括心理、情感、技能和知识等。根据查阅到的资料,以下是一些具体的方法和步骤,帮助你快速提升自己: 1. 培养屏蔽力 荷兰畅销书作家罗伊马丁纳提到,屏蔽力是个人成长…...
题目:一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。
题目:一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence…...
《HelloGitHub》第 96 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 …...
C++tuple类型
tuple 类型 tuple是类似pair的模板。 每个pair的成员类型都不相同,但每个pair都恰好有两个成员。不同tuple类型的成员类型也不相同,但一个tuple可以有任意数量的成员。 每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可…...
亚远景科技-浅谈ASPICE标准和ASPICE认证/评估
ASPICE(Automotive SPICE)是一种针对汽车行业的软件开发过程的评估模型,它旨在帮助汽车制造商和供应商提高软件开发过程的能力和质量,从而提升产品的质量、安全性和效率。 ASPICE标准涵盖了软件开发的各个阶段和活动,…...
PHP性能提升方案
一、背景与介绍 PHP语言开发效率高,特别应用于适合中小型项目,对于创业初期敏捷开发验证项目可行性或者Demo演示绝对占据优势。 但是随着现在Web应用的复杂性,针对项目要适应高并发、高流量的访问特性,PHP确实在性能方面相对Go、J…...
关系(二)利用python绘制热图
关系(二)利用python绘制热图 热图 (Heatmap)简介 热图适用于显示多个变量之间的差异,通过颜色判断彼此之间是否存在相关性。 快速绘制 基于seaborn import seaborn as sns import pandas as pd import numpy as np i…...
P8597 [蓝桥杯 2013 省 B] 翻硬币
# [蓝桥杯 2013 省 B] 翻硬币 ## 题目背景 小明正在玩一个“翻硬币”的游戏。 ## 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo&#x…...
主流公链 - Fantom
Fantom:高性能的区块链协议 Fantom是一种开创性的区块链协议,旨在革新去中心化应用和数字金融领域 技术特点 共识机制 Lachesis协议:Fantom使用了Lachesis协议作为其共识算法。Lachesis是一种 异步拜占庭容错(ABFT)共…...
vue-quill-editor 富文本编辑器(可上传视频图片),组件挂载的方式实现
1.安装 npm install vue-quill-editor --save npm install quill-image-drop-module --save npm install quill-image-resize-module --save2.在组件下面新增组件 QlEditor (1)index.vue <template><div><div idquillEditorQiniu><!-- 基于element…...
入门编程第一步,从记住这些单词开始
** 入门编程第一步,从记住这些单词开始 ** 2023-10-18 一、交互式环境与 print 输出 1、print : 打印/输出 2、coding : 编码 3、syntax : 语法 4、error : 错误 5、invalid : 无效 6、idenfifier : 名称/标识符 7、character : 字符 二、字符串的操作&#x…...
[C++]使用OpenCV去除面积较小的连通域
这是后期补充的部分,和前期的代码不太一样 效果图 源代码 //测试 void CCutImageVS2013Dlg::OnBnClickedTestButton1() {vector<vector<Point> > contours; //轮廓数组vector<Point2d> centers; //轮廓质心坐标 vector<vector<Point&…...
vscode连接不上,终端ssh正常,一直输入密码正确但是无法登录
若是之前链结果突然等不上,使用第一个链接 若是第一次链接连不上,先使用第二个链接,在使用第一个链接 原因:原因是服务器端的wget命令不能使用,vscode需要服务器端下载个文件,无法下载就导致了如上的错误…...
医疗生成式AI伦理挑战与TREGAI评估清单:从原则到实践
1. 医疗领域生成式AI的伦理挑战与TREGAI评估清单生成式人工智能(Generative AI, GenAI)正在以前所未有的速度重塑医疗健康领域。从ChatGPT撰写病历摘要,到GAN(生成对抗网络)合成医学影像用于数据增强,再到扩…...
众包平台AI渗透率达33-46%:人机协同如何重塑文本任务生态
1. 项目概述:一场关于“AI渗透率”的田野调查最近,一份关于众包平台中大型语言模型使用率的调查报告,在圈内引发了不小的讨论。报告的核心结论很直接:在当前的文本类众包任务中,大约有33%到46%的产出,背后都…...
CANN SHMEM工具调测指南
SHMEM搭配工具算子调测指导 【免费下载链接】shmem CANN SHMEM 是面向昇腾平台的多机多卡内存通信库,基于OpenSHMEM 标准协议,实现跨设备的高效内存访问与数据同步。 项目地址: https://gitcode.com/cann/shmem msprof shmem后续会适配msprof算子…...
#24 Agent 的浏览器自动化:Playwright、Selenium 与网页交互
从一次凌晨三点的事故说起 去年冬天,我负责的一个自动化脚本在凌晨三点突然崩了。日志里只有一行:ElementClickInterceptedException。点一个“确认”按钮,被一个弹窗遮住了。Selenium 的 WebDriverWait 等了十秒,弹窗刚好在点击前…...
终极视频加速指南:如何使用Video Speed Controller提升学习与工作效率
终极视频加速指南:如何使用Video Speed Controller提升学习与工作效率 【免费下载链接】videospeed HTML5 video speed controller (for Google Chrome) 项目地址: https://gitcode.com/gh_mirrors/vi/videospeed 在当今信息爆炸的时代,视频已成为…...
AetherFloat浮点架构:AI加速器的硬件革新与优化
1. AetherFloat浮点架构:AI加速器的硬件革新在AI加速器设计中,浮点计算单元一直是性能与能效的关键瓶颈。传统IEEE 754浮点标准虽然已成为通用计算的基石,但其在神经网络处理器(NPU)中的实现却面临诸多挑战:深层的对数移位器消耗大…...
【Nginx】如何集成 Prometheus + Grafana 监控 Nginx?—— 从原理到生产落地的完整指南
如何集成 Prometheus + Grafana 监控 Nginx?—— 从原理到生产落地的完整指南 适用读者:已部署过基础 Nginx 服务、了解反向代理,但尚未系统掌握其可观测性建设的中高级后端或 SRE 工程师。 技术栈:Nginx 1.24+(开源版)、Prometheus 2.40+、Grafana 10+、Docker 24+ 一、…...
Gitee崛起:CODING停服后的国产DevOps新选择
腾讯CODING DevOps系列产品的停服公告在开发者社区引发震动,这一决定将直接影响数百万开发者和数千家企业用户的日常研发工作。作为中国领先的源代码托管平台,Gitee凭借其本土化优势、完善的功能生态和活跃的开源社区,正迅速成为市场关注的焦…...
Aegis-Veil:基于Linux命名空间的桌面应用沙箱隔离实践
1. 项目概述:Aegis-Veil 是什么,以及它解决了什么问题如果你在开源社区里混迹过一段时间,尤其是对系统安全、隐私增强或者沙箱技术感兴趣,那么你很可能已经听说过smouj/Aegis-Veil这个项目。乍一看这个标题,可能会觉得…...
电气仿真与机电协同设计的关键技术与应用
1. 电气仿真在现代机电系统设计中的核心价值十年前我刚进入汽车电子行业时,设计验证还主要依赖物理样机和"烧板子"的土办法。记得有次因为一个继电器选型错误,导致整车电气系统在-30℃环境下集体罢工,公司为此损失了上千万的召回成…...
