@Around踩坑记录

news2024/9/28 1:08:24

@Around踩坑记录

先上结论:

如果你在定义切面的时候打算使用环绕通知➕定义注解的方式,那么在进行*@Around*("(@annotation(costTrace))") 类似这样的定义的时候,”costTrace“一定要与你定义切面中入参的参数名字一致,而不是参数类型(你定义的注解名字)一致.

1. 背景

在完善自己暑期实习项目的过程中,发现自己在业务逻辑中花了大量的篇幅去打一些和方法耗时计算相关的日志,于是在优化的过程中,第一步便是利用切面编程的思想将这些计算耗时的日志代码通过切面的方式,悄无声息的侵入,把代码篇幅都留给业务逻辑,而不是这些千篇一律的日志打印。

在这里,我通过传统的 (自定义注解 ➕ spring AOP 切面)的方式进行,以下是我的初始版本⬇️

1.1 引入依赖与定义配置


<dependency> 
  <groupId>org.aspectj</groupId>  
  <artifactId>aspectjrt</artifactId> 
</dependency>  
<dependency> 
  <groupId>org.aspectj</groupId>  
  <artifactId>aspectjweaver</artifactId> 
</dependency>

spring的xml配置文件中定义:

<aop:config proxy-target-class="true"/>
<aop:aspectj-autoproxy/>

1.2 自定义注解

/**
 * @author kaihua
 * @describe : 被该注解标记,将被进行代价计算与日志打印
 * @date 2023/7/4
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD })
public @interface CostTrace {

    String value() default "";

}

1.3 定义切面

/**
 * @author kaihua
 * @describe
 * @date 2023/7/4
 */
@Aspect
@Component
public class CostTraceAspect {

    /**
     * CostTrace 切面
     * @param joinPoint : 切点
     * @param costTrace : 方法注解
     * @return : 返回值
     */
    @Around("(@annotation(CostTrace))")
    public Object handleJoinPoint(ProceedingJoinPoint joinPoint, CostTrace costTrace) {
        //1. 获取切入点方法信息
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        String className = methodSignature.getDeclaringType().getName();
        String methodName = methodSignature.getName();
        String costTraceInformation = costTrace.value();
        Object[] args = joinPoint.getArgs();
        String requestStr = JSON.toJSONString(args);

        //2. 定义方法开始时间
        long begin = System.currentTimeMillis();
        Object result = null;

        try {
            //3. 执行方法
            result = joinPoint.proceed(args);

            //4.1 方法执行完毕之后打印日志
            LoggerManager.info(
                    String.format(
                            "the method %s of class %s execution successful, the input parameters are %s, the total time consumption is %dms, the other information: %s"
                            , methodName
                            , className
                            , requestStr
                            , System.currentTimeMillis() - begin
                            , costTraceInformation)
                    ,""
                    ,"100");
        } catch (Throwable e) {
            e.printStackTrace();
            //4.2 方法若执行失败打印日志
            LoggerManager.error(
                    String.format(
                            "the method %s of class %s execution error, the input parameters are %s, the other information: %s"
                            , methodName
                            , className
                            , requestStr
                            , costTraceInformation)
                    ,""
                    ,e);
        }

        return result;
    }
}

2. 问题的出现

在预发环境部署的过程中出现了如下报错

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dependencyTypeHolder' defined in URL [jar:file:/home/admin/taefileserver/target/taefileserver.war/WEB-INF/lib/ateye-client-2.3.8.jar!/ateye-scene.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.config.internalTransactionAdvisor': Cannot resolve reference to bean 'org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0' while setting bean property 'transactionAttributeSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0': BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0': Cannot resolve reference to bean 'cursitor-pointcut' while setting bean property 'pointcut'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cursitor-pointcut' defined in class path resource [spring-cursitor.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#1': Cannot resolve reference to bean 'jmonitor-pointcut' while setting bean property 'pointcut'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jmonitor-pointcut' defined in class path resource [jmonitor-spring.xml]: Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut

报错非常长,但是通过控制变量法(通过部署前后的日志变化比较,不推荐,,最笨的方法)以及读取日志内容(推荐,但是有时候难以排查问题)发现,日志中pointcut的字眼,并且报错的本质原因是bean容器初始化失败,那必定是某个bean的定义出现了问题。

结合自己新添加的代码,发现只有自己定义的切面被标注了@Component,便把问题锁定在切面中了,但是这段代码乍一看,并没有什么错误,就是一个非常简单的环绕通知定义。

3. 问题的排查

通过复制日志报错搜索解决方案显然不太可行,后面专门去搜索了@Around配合注解如何去进行定义,终于在廖雪峰老师的网站看到了答案.

👉 https://www.liaoxuefeng.com/wiki/1252599548343744/1310052317134882

请添加图片描述

我又重新尝试修改了自己定义的切面的代码,进行部署,终于成功了!!

但是我这里还是不明白,为什么不能够使用注解类型,非要去使用对应的注解参数名字呢?

在一番搜索和chatGpt battle大战之后,大概搞清楚了原因:

**“在使用 @annotation 拦截注解时,需要获取到该注解的属性值,而注解的属性值是在注解对象中定义的,而不是在注解类型中定义的。因此,在 @annotation 中传入注解对象,可以获取到注解的属性值。”**

4. 解决方法的证实

我将切面的@Around定义分别换为了两个版本,结果都可以部署成功,基本可以证明了这个做法的正确性。

// version 1
@Around("(@annotation(costTrace))")
public Object handleJoinPoint(ProceedingJoinPoint joinPoint, CostTrace costTrace)

// version 2
@Around("(@annotation(cos))")
public Object handleJoinPoint(ProceedingJoinPoint joinPoint, CostTrace cos)

5. 小结

虽然这个只是一个非常非常非常小的排查问题案例,但是还是不由得产生非常多的感悟:

“作为一个臭写代码的程序员,我们所谓的学习,只是在学习其他人定义的规则,最典型的就是开发过程中的各种框架,我们在不断的学习别人定下来的规则。虽然这个现实是绝望的,但是我们也要从中有所收获,我们今天了解了这个规则,并不应该仅仅停留于学习到了这个规则,更可贵的是要去尝试去思考规则制定者在制定规则时候的思考”

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

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

相关文章

【嵌入式Qt开发入门】Qt如何使用多线程——初识QThread

我们写的一个应用程序&#xff0c;应用程序跑起来后一般情况下只有一个线程&#xff0c;但是可能也有特殊情况。比如我们前几篇的例程都跑起来后只有一个线程&#xff0c;就是程序的主线程。线程内的操作都是顺序执行的。但试着想一下&#xff0c;我们的程序顺序执行&#xff0…

模拟日志数据

模拟日志数据。。 日志模拟器主要模拟站点的用户信息和用户行为日志数据 使用shell调用java类实现数据的生产 ! 所需文件如下: datagen.jar genlog.sh 注意: 存储数据的文件夹要预先创建好 Bash mkdir -p /tmp/doit/user_data mkdir -p /tmp/doit/log_data 将用来模拟生成…

【安全】Xsslabs(1~13)基于白盒测试浅析

目录 环境 关卡 level 1 level 2 level 3 level 4 level 5 level 6 level 7 level 8 扩展 level 9 level 10 level 11 level 12 level 13 总结 环境 PHP&#xff1a;php7.3.4nts 中间件&#xff1a;Nginx1.15.11 工具&#xff1a;Hackbar 关卡 level …

超长上下文处理:基于Transformer上下文处理常见方法梳理

原文链接&#xff1a;芝士AI吃鱼 目前已经采用多种方法来增加Transformer的上下文长度&#xff0c;主要侧重于缓解注意力计算的二次复杂度。 例如&#xff0c;Transformer-XL通过缓存先前的上下文&#xff0c;并允许随着层数的增加线性扩展上下文。Longformer采用了一种注意力…

【GLBCXX_3.4.21 not found】问题解决

问题描述 rootlocalhost:/home# rar -h rar&#xff1a;/lib64/libstdc.so.6 ,version "GLBCXX_3.4.21" not found 问题排查过程 下载相应的GCC源码&#xff0c;找到适合自己版本&#xff0c;这里选择是的是7.3.0 gcc源码下载地址&#xff1a;http://ftp.gnu.org/g…

fastadmin数据内容admin_id权限

/*** 是否开启数据限制* 支持auth/personal* 表示按权限判断/仅限个人* 默认为禁用,若启用请务必保证表中存在admin_id字段*/protected $dataLimit true;/*** 数据限制字段*/protected $dataLimitField admin_id;

第一次上传GitHub代码------教程、记录和踩雷

刚录用的一篇论文&#xff0c;编辑要求上传GitHub repo。 对于上传代码&#xff0c;本人是一无所知&#xff0c;写这篇博客记录一下 一. 创建代码仓库 点击右上角&#xff0c;选择New repository&#xff0c;来创建仓库&#xff1a; 根据提示&#xff0c;填写仓库的名称、描述…

TCP 协议(二)连接与断开

TCP 连接概述 TCP 协议是一种面向连接的、可靠的数据传输协议&#xff0c;同时 TCP 连接是全双工的&#xff0c;即连接的两端可以互传数据。在深入了解 TCP 连接之前&#xff0c;我们先来弄清楚整个 TCP 连接的过程&#xff0c;之后在深入整个数据报文结构来认识 TCP。 TCP连…

全网最全,adb常用命令大全(详细)全覆盖,看这篇就够了..

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 adb 模拟按键输入…

python subprocess执行外部命令常用方法

subprocess模块是Python标准库中的一个模块&#xff0c;用于创建和控制子进程。它提供了一种在Python程序中调用其他外部命令、执行系统命令和与系统进程进行交互的方法。常用的有两种方法&#xff1a;subprocess.run()&#xff0c;subprocess.Popen() 1. subprocess.run()方法…

回顾类与对象:掌握String探索其模拟实现的沉浸式体验

目录 一.STL简介二.string的模拟实现1.成员变量与(拷贝)构造、析构函数2.运算符重载[ ]3.添加数据与扩容4.赋值运算符重载及其他重载5.其他函数 一.STL简介 标准模板库 STL是C标准库的重要组成部分&#xff0c;stl分为六大组件&#xff1a;算法、容器、迭代器、空间适配器、仿…

NLP实战7:seq2seq翻译实战-Pytorch复现

&#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] &#x1f4cc; 本周任务&#xff1a; ●请根据N5、N6周内容&#xff0c;为解码器添加上注意力机制 一、前期准备…

常用分类损失CE Loss、Focal Loss及GHMC Loss理解与总结

一、CE Loss 定义 交叉熵损失&#xff08;Cross-Entropy Loss&#xff0c;CE Loss&#xff09;能够衡量同一个随机变量中的两个不同概率分布的差异程度&#xff0c;当两个概率分布越接近时&#xff0c;交叉熵损失越小&#xff0c;表示模型预测结果越准确。 公式 二分类 二…

【QT】QT搭建OpenCV环境

QT/OpenCV 01、开始之前02、QT03、CMake04、OpenCV05、配置06、测试 01、开始之前 本文版本&#xff1a; 1、QT&#xff1a;Based on Qt 5.12.2 (MSVC 2017, 32 bit)&#xff0c;编译方式是MinGW 2、CMake&#xff1a;cmake-3.27.0-rc4-windows-x86_64.msi 3、OpenCV&#xff1…

2023年值得入手的开放式耳机推荐,蓝牙耳机的选购指南分享推荐

身为一个音乐爱好者&#xff0c;出于对音质和佩戴舒适的追求&#xff0c;也有入手了很多品类的耳机&#xff0c;其中不乏有有线耳机、无线蓝牙耳机&#xff0c;两种不同的音频传输方式大类&#xff0c;其各自所拥有的特性也是不同的。而居于后者的无线蓝牙耳机&#xff0c;在现…

【Java基础教程】(八)面向对象篇 · 第二讲:Java 数组全面解析——动态与静态初始化、二维数组、方法参数传递、排序与转置、对象数组、操作API~

Java基础教程之面向对象 第二讲 本节学习目标1️⃣ 概念1.1 动态初始化1.2 静态初始化 2️⃣ 二维数组3️⃣ 数组与方法参数的传递4️⃣ 数组排序5️⃣ 数组转置6️⃣ 对象数组7️⃣ 数组操作API7.1 数组复制7.2 数组排序 &#x1f33e; 总结 本节学习目标 掌握数组的动态及静…

水库监测中仪器安装及监测结果的要求有哪些

水库监测点位布设需要根据水库运行情况和安全监测的需求来进行&#xff0c;一般分为基础监测点位和重要部位监测点位&#xff0c;基础监测点位主要包括上游水位、上游库水位变幅、库岸稳定以及上下游坝坡稳定等。重要部位监测点位主要包括坝轴线、溢洪道进口和泄水洞出口等部位…

前端报错:“Uncaught SyntaxError: missing ) after argument list“只是参数列表后面缺少 “)”?

报错"Uncaught SyntaxError: missing ) after argument list"&#xff0c;字面翻译过来的意思&#xff1a;语法错误: 参数列表后面缺少 )。 一直以为是少了 一个小括号找了好久 发现并不是 据提示是参数列表的问题&#xff0c;找到文件中存在参数列表的地方。如下图…

如何利用MyBatis完成web项目的环境搭建(导入核心依赖包、日志、编译环境,配置文件以及Druid连接池)

目录 项目环境搭建 servlet实例 核心依赖 导入日志 编译环境 mapper注册 resouces中 dao中 MyBatis配置文件 实例效果 导入配置文件 Druid连接池 Druid连接池是什么&#xff1f; 如何配置Druid连接池&#xff1f; 实体类 实例效果 项目环境搭建 1.在pom.xml中…

STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059

STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059 Proteus仿真小实验&#xff1a; STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059 功能&#xff1a; 硬件组成&#xff1a;51单片机 8位数码管MAX7219数码管驱动模块多个按键LED灯蜂鸣器 1.准确测量…