Aop面向切面实现开发日志收集打印一文轻松搞定,内附详细图文示例+源码自取

目录

介绍

动态代理

jdk动态代理

cglib动态代理

注解实现Aop 

添加必须依赖

添加Atm类 (主业务逻辑代码块)

 定义打印log方法(提取公共代码逻辑块)

启用代理 

切点表达式

Aop通知类型 

前置通知(@Before)

后置通知(@After)

正常结束通知(@AfterReturning)

异常结束通知(@AfterThrowing)

环绕通知

 切面的优先级

 Aop使用注意点

方法权限不能是private

其他方法在内部被调用时不会被增强

注解实现Aop

基于注解实现服务层打印入参和返回参数日志

未使用Aop效果

使用注解实现Aop入参返回值等日志打印

示例源码


介绍

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,可以在不改变原有代码的情况下,通过在程序的各个关键点上增加切面(Aspect)的方式,实现对代码的增强和横切关注点的分离,从而提高代码的可重用性、可维护性和可扩展性。

AOP的核心思想是将程序中的不同关注点进行解耦,避免不同的关注点相互嵌入,导致代码冗长、难以维护的问题。通过将不同的关注点抽象成切面,实现了程序的层次性,使得不同层次中的各种关注点可以独立地进行开发、管理和维护。

在实现AOP时,需要定义切点(Pointcut)和切面(Aspect)两个概念。切点用于定义程序中需要增强的关键点,例如方法的调用、异常抛出、对象的初始化等等。切面则是对切点进行增强的具体实现,例如日志记录、性能监测、事务管理等等。AOP框架通过在程序中动态生成代理对象的方式,将切面织入到切点上,从而实现对程序的增强。

在Java中,常用的AOP框架包括Spring AOP和AspectJ。Spring AOP是基于代理的AOP框架,可以通过配置文件或注解的方式实现切面的定义;而AspectJ是基于注解的AOP框架,可以直接在Java代码中使用注解的方式定义切点和切面。

动态代理

动态代理是一种用于实现面向切面编程的技术。它允许您编写一些代码来控制某个类或接口的行为。它可以在不更改原始代码的情况下改变或增强类的行为。当类的对象被创建时,动态代理会在内存中创建一个新的类和一个代理类,以控制原始类的行为。

下面看一个代码示例:

 测试

可以看到这里两个方法除了各自方法的核心业务逻辑外,方法执行前和执行后是重复的非核心业务代码,这里方法只有两个,如果方法成百上千时就需要改很多的地方,从而可以使用动态代理进行优化处理

jdk动态代理

JDK动态代理是通过Java自带的反射机制实现的,主要涉及两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

Proxy类是JDK提供的动态代理类,它提供了用于创建动态代理对象的静态方法newProxyInstance()。这个方法有三个参数:

  1. ClassLoader对象,用于指定动态代理类的ClassLoader,一般使用被代理对象的ClassLoader
  2. Class<?>[]对象数组,用于指定被代理类实现的接口;
  3. InvocationHandler对象,用于指定动态代理对象的方法调用处理器。

InvocationHandler接口是一个函数式接口,它只有一个invoke()方法,用于处理动态代理类的方法调用。invoke()方法有三个参数:

  1. Object对象,表示被代理对象;
  2. Method对象,表示方法对象;
  3. Object[]数组,表示方法参数。

当动态代理类的方法被调用时,JVM会自动调用代理对象的invoke()方法,将方法名、方法参数等作为参数传入该方法。在invoke()方法中,我们可以通过反射机制执行被代理类中的相应方法,并对方法的返回值进行处理和返回。

总体来说,JDK动态代理是在运行时动态生成一个类,并在该类中实现代理接口中的方法。当该类的方法被调用时,JVM会调用invoke()方法,从而实现代理方法的处理和返回。

执行测试:

 这样的话后续再新增新的业务方法时只需要进行接口方法的核心业务的书写,而不需要再关注非核心业务代码的处理,非核心业务的代码统一在代理对象中处理即可

cglib动态代理

CGLib 动态代理的实现原理主要是利用 ASM 字节码操作库和 Java 反射机制,在程序运行时动态地生成一个新类。这个新类继承自被代理的类,并覆盖掉所有非 final 方法,然后在这些方法中插入额外的操作(如日志记录、权限验证、事务控制等),达到动态代理的目的。 CGLib 通过子类化的方式来实现代理,所以它只能代理出接口的实现类,而不能直接代理接口。CGLib 使用 ASM 框架直接读取 Class 文件,并对其进行转换,从而生成所需的子类,生成的代理对象可以直接访问到被代理类的所有属性和方法。而且由于它是子类化的,所以即使是没有接口的类也可以代理。因此,CGLib 动态代理的应用范围更加广泛。 由于 CGLib 实现的是通过字节码技术产生的子类来实现代理行为,所以代理速度更快,但也存在一定的局限性,例如被代理类不能是 final 类型的、代理的目标方法不能是 final 的等。

下面用cglib来实现前面的示例

 执行测试:

注解实现Aop 

添加必须依赖

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version>
</dependency>

添加Atm类 (主业务逻辑代码块)

 定义打印log方法(提取公共代码逻辑块)

启用代理 

测试:

 

切点表达式

Aop通知类型 

前置通知(@Before)

在连接点方法执行之前的增强处理

前面演示的demo就是使用的前置通知@before,这里不再演示

后置通知(@After)

在连接点方法执行之后的增强处理,无论正常结束还是异常结束,都会执行的处理

调整atm方法

添加后置执行方法

执行测试:

手动调整异常再进行测试

正常结束通知(@AfterReturning)

在连接点方法正常结束后会进行的处理   如果方法有返回值,可以拿到方法的返回值

添加正常结束通知

刚才前面的例子中我们传入参数100时是手动定义的一个异常,那么这里添加一个正常结束通知不会执行才对,测试看下

 调整参数,不抛出异常观察正常通知是否执行

获取正常通知参数

 可以看到这里的方法是有返回值,可以通过正常通知来获取该返回值,从而做进一步的业务处理

再执行测试

异常结束通知(@AfterThrowing)

在连接点方法异常结束后会进行的处理  可以获取异常的信息

添加异常结束通知方法

此时先执行一个不会抛异常的方法观察异常结束通知会不会执行

可以看到正常结束通知执行了,异常结束通知没有执行

调整参数为100的异常执行,查看异常通知方法的执行情况

 可以查看到异常结束通知执行,正常结束通知没有执行

获取异常信息

执行异常测试

 注意此处的异常信息只是捕获到了,没有做任何的处理

环绕通知

通过代码调用方法,在方法的执行周围进行增强

先注释掉之前的通知方法

添加环绕通知

先进行异常测试

进行正常结束测试

 注意:

当有前置后置以及环绕通知时,先进行环绕通知,在方法的具体执行前后进行增强

放开所有通知方法进行测试

注意观察通知执行的顺序

 切面的优先级

当我们的程序中定义了多个切面时,可以通过@Order(数字)来控制各个切面的执行顺序,其中数字越小,执行优先级越高

话不多说,直接上代码

在之前原先的基础上再定义一个切面类

日志信息也稍作调整以做区分,Order设为1,

原先的log切面Order设置为2

 启动测试查看执行优先级

可以观察到优先级顺序

 Aop使用注意点

方法权限不能是private

连接点方法不能是private,会导致Aop不能进行增强

前面举例时的take是public的aop可以进行正常增强,那么如果调整为private,再进行测试看看:

测试 

 Aop增强失效

如果是protected呢

protected也可以进行aop增强

其他方法在内部被调用时不会被增强

直接上代码,这里直接调用两个方法来进行增强

 此时两个方法都被增强

如果在取钱方法中调用了存钱方法,观察此时的增强通知会执行几次

测试

 可以看到增强通知只执行了一次,且只执行了取钱方法的增强通知

注解实现Aop

基于注解实现服务层打印入参和返回参数日志

在业务开发中我们有时需要通过日志打印入参参数和方法返回,但是基本传统写法都是在方法中自定义log日志,这样写其实并不太优雅,可以通过aop进行优化日志打印

未使用Aop效果

新建两个实体模拟存储保存订单Do和更新订单Do

创建服务层模拟业务

 调用接口查看打印日志

可以看到只是执行了服务层的主业务核心代码System.out.println模拟的业务代码,如果还想打印传参和返回值信息就需要使用日志打印log.info("日志信息")等

可以使用Aop进行业务优化

使用注解实现Aop入参返回值等日志打印

新建切面注解

 新建统一转换参数

新建saveOrder和updateOrder转换Operate

 

定义主要切面类

import cn.hutool.json.JSONUtil;
import com.example.demo23.demos.web.annotation.MyLogOperate;
import com.example.demo23.demos.web.service.Convert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Component
@Aspect
public class LogAsept {// 定义切入点@Pointcut("@annotation(com.example.demo23.demos.web.annotation.MyLogOperate)")public void pointcut(){}private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,1,1,TimeUnit.SECONDS,new LinkedBlockingDeque<>());//环绕通知@Around("pointcut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{Object obj = proceedingJoinPoint.proceed();threadPoolExecutor.execute(()->{try{MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();MyLogOperate myLogOperate = methodSignature.getMethod().getAnnotation(MyLogOperate.class);Class<? extends Convert> convert = (Class<? extends Convert>) myLogOperate.convert();Convert logConvert = convert.newInstance();OperateLogDo operateLogDo = logConvert.convert(proceedingJoinPoint.getArgs()[0]);operateLogDo.setDesc(myLogOperate.desc()).setResult(obj.toString());System.out.println("插入 operateLog" + JSONUtil.parseObj(operateLogDo));}catch (InstantiationException ex) {throw new RuntimeException(ex);} catch (IllegalAccessException ex) {throw new RuntimeException(ex);}});return obj;}
}

服务层添加注解使用aop进行日志切入

启动测试:

切入成功,打印入参和方法返回值成功

 还可以再详细一些打印出接口的调用时间,方法,路径等

示例源码

需要源码的伙伴在下面自取即可

链接:aop示例源码 
提取码:z2wp 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树莓派上使用Nginx通过内网穿透实现无公网IP访问内网本地站点

前言 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Nginx 的内存占用率非常低&#xff0c;可以在树莓派等资源受限的设备上运行。同时结合c…

如何让大模型更好地完成知识图谱推理?

​ 论文标题&#xff1a; Making Large Language Models Perform Better in Knowledge Graph Completion 论文链接&#xff1a; https://arxiv.org/abs/2310.06671 代码链接&#xff1a;GitHub - zjukg/KoPA: [Paper][Preprint 2023] Making Large Language Models Perform Be…

node-red - 节点实战总结1

node-red - 节点实战总结1 二、功能2.1 循环(for\while) 三、网络四、序列五、解析六、存储七、协议7.1 modbus协议7.2 opcua 八、formats8.1 时间格式化与时区转换 二、功能 2.1 循环(for\while) 安装节点node-red-contrib-loop-processing,该节点支持三种方式的循环&#xf…

【SpringBoot】 This application has no explicit mapping for 解决方法

This application has no explicit mapping for 解决方法 This application has no explicit mapping for 解决方法一、背景二、原因三、解决方案方式一&#xff1a;方式二&#xff1a; 四、解决 This application has no explicit mapping for 解决方法 一、背景 在SpringBo…

奥特曼不是第一次被开除!离职YC系“被创始人要求离开”

明敏 西风 发自 凹非寺 量子位 | 公众号 QbitAI 钮祜禄奥特曼&#xff0c;竟然不是第一次被“扫地出门”&#xff1f;&#xff1f;&#xff01; 没想到&#xff0c;OpenAI闹剧刚稍微消停了一点&#xff0c;“前传”马上来了。 《华盛顿邮报》从知情人士处获悉&#xff0c;奥…

java编程:使用递归 循环和位运算实现将10进制转为2进制

1 递归 /*** 递归&#xff1a;十进制转二进制* param decimal 待转换的十进制数* param binary 转换后的二进制数*/public static void decimalToBinaryByRecursion(int decimal,StringBuilder binary){if(decimal < 0){return;}decimalToBinaryByRecursion(decimal/2,bina…

3D卷积的理解

卷积核不仅需要在高宽这两个维度上进行滑动&#xff0c;还需要在时间维度上进行滑动

前端设计问题:iframe

居中问题&#xff1a; 尝试了一般的居中方法&#xff0c;无效果 display: flex;justify-content: center;align-items: center;放到导航栏下面不居中 放到页面底部还是不居中 Code <iframe id"demo_sanshui" src"demo_sanshui.html" width"120%…

[HCIE] IPSec-VPN (IKE自动模式)

概念&#xff1a; IKE&#xff1a;因特网密钥交换 实验目标&#xff1a;pc1与pc2互通 步骤1&#xff1a;R1与R3配置默认路由 R1&#xff1a; ip route-static 0.0.0.0 0.0.0.0 12.1.1.2 R2&#xff1a; ip route-static 0.0.0.0 0.0.0.0 23.1.1.2 步骤2&#xff1a;配ACL…

工业级5G路由器:稳定性更高,网络速度更快!

随着5G技术的发展&#xff0c;5G路由器也越来越受到人们的关注。特别是工业级5G路由器&#xff0c;它的应用范围更广&#xff0c;稳定性更高&#xff0c;网络速度更快&#xff0c;已成为许多企业和工业领域的必备选择。 一、工业级5G路由器的特点 工业级5G路由器具有很多独特的…

利用MATLAB进行矩阵运算

一、画出y1/(x3)的函数曲线&#xff0c;x∈[0, 200]。 程序&#xff1a; x0:0.01:200; y(3x).^(-1); plot(x,y) 结果&#xff1a; 二、生成一个信号&#xff1a;xsin(2*pi*t)cos(4*pi*t) 程序&#xff1a; syms t; xsin(2*pi*t).*cos(4*pi*t); fplot(x,[0 pi]); 结果&…

electerm 跨平台的终端 /ssh/sftp 客户端

文章目录 electerm功能特性主题配色 electerm 每个程序员基本都离开SSH链接工具,目前市场上好用的基本都是收费的 给大家推荐一款国人开发的开源链接工具https://github.com/electerm/electerm 到目前为止star已经9.5K了,非常受欢迎 功能特性 支持ssh,telnet,serialport,本地和…

3ds Max 电脑配置建议 | 建模+渲染选专业显卡or游戏显卡?

&#xfeff;使用3ds Max进行建模和渲染时&#xff0c;选择合适的电脑配置非常重要。比如在硬件选择上&#xff0c;究竟选购游戏显卡还是专业显卡呢&#xff1f;本文将为你详细介绍游戏显卡和专业显卡的区别&#xff0c;并提供配置建议&#xff0c;助你作出明智的决策。 &#…

手把手用GPT开发小程序全流程!就是这么easy~

大家好&#xff0c;我是五竹。 前段时间用GPT开发了一款小程序:GPT真牛批&#xff01;三天开发一个小程序&#xff0c;三天积累了2000的用户&#xff0c;上周末抽空又接入了流量主&#xff0c;感兴趣的同学可以围观一下。 今天就来带大家走一遍用GPT开发一款小程序的全过程&a…

为什么选择美国VPS服务器

企业、个人和组织都需要一个稳定高效的服务器来托管他们的网站、应用程序和数据。而对于中国用户来说&#xff0c;寻找一个性价比高的便宜美国VPS服务器&#xff0c;既能满足需求&#xff0c;又能节约成本&#xff0c;成为了一个非常重要的问题。 VPS即虚拟专用服务器&#xf…

C++算法 —— 贪心(3)

文章目录 1、买卖股票的最佳时机2、买卖股票的最佳时机Ⅱ3、K次取反后最大化的数组和4、按身高排序5、优势洗牌6、最长回文串7、增减字符串匹配 1、买卖股票的最佳时机 121. 买卖股票的最佳时机 这里最容易想到的就是暴力枚举&#xff0c;两层for循环&#xff0c;i 0&#xf…

TFA-Net

TFA SCA means ‘Self-Context Aggregation’ 作者未提供代码

一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; &#x1f383; 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲 &#x1f3f7;️ 标签 | 男 自律狂人 目标明确 责任心强 ✈️公…

羊大师教你,什么搭配羊奶能够带来全方位的营养?

羊奶作为一种营养价值极高的乳制品&#xff0c;其丰富的营养成分对人体健康有着诸多益处。然而&#xff0c;不同的食物搭配会对羊奶的营养吸收产生不同的影响。为了让大家更好地利用羊奶的营养价值&#xff0c;下面小编羊大师将为大家介绍一些与羊奶搭配的食物&#xff0c;帮助…