架构(十五)Java字节码增强

一、引言

        一般如果需要做增强类的架构工具会使用SpringBoot提供的切面,但是这逃不开两个问题:1、使用方需要加注解代码;2、版本更新导致的发布。

        所以java还提供了字节码层面的增强方案,对使用的系统是无感的。

二、字节码增强选型

1、Java Agent简介

        Java Agent 是一种 Java 技术,它允许开发者在应用程序运行时修改或增强 Java 字节码。字节码增强是指通过修改字节码来改变现有类的行为或添加新的功能。        
        Java Agent 通过使用 Java Instrumentation API 来实现字节码增强。它允许开发者在类加载过程中拦截和修改字节码,从而实现对类的增强。通过 Java Agent,开发者可以在不修改源代码的情况下,对已有的类进行功能扩展、性能优化、调试等操作。
        字节码增强可以用于各种用途,例如:
1. AOP(面向切面编程):通过在方法前后插入额外的代码,实现日志记录、性能监控、事务管理等功能。
2. 动态代理:通过修改字节码,在运行时生成代理对象,实现对目标对象的拦截和增强。
3. 字节码注入:在类加载过程中修改字节码,实现对类的功能扩展或修复。
4. 代码热替换:在应用程序运行时,动态修改已加载类的字节码,实现代码的热部署和更新。
        在实际应用中,通常会使用一些开源的字节码增强框架,如 ASM、Byte Buddy、Javassist 等,来简化字节码操作的复杂性。

2、选型比较

1、ASM(ObjectWeb ASM)


ASM是一个低级别的字节码操作库,提供了直接访问和修改字节码的功能。
它提供了灵活的API,可以对字节码进行细粒度的操作,但使用起来相对较复杂。
ASM的性能很高,因为它直接操作字节码,没有额外的开销。
ASM通常用于底层的字节码操作,如编写字节码插桩工具、静态分析工具等。



2. Byte Buddy:


Byte Buddy是一个高级别的字节码操作库,提供了更简单和易于使用的API。
它使用了更高级的抽象,可以通过编写Java代码来定义和修改字节码。
Byte Buddy支持动态生成代理对象、修改现有类的行为等常见的字节码增强操作。
Byte Buddy的性能较好,并且具有较好的易用性和灵活性。



3. Javassist:


Javassist是一个中级别的字节码操作库,提供了方便的API来修改字节码。
它使用类似于Java源代码的语法,可以通过编写类似于Java代码的字符串来定义和修改字节码。
Javassist支持动态生成代理对象、修改现有类的行为等常见的字节码增强操作。
Javassist的性能较好,并且具有较好的易用性和灵活性。

        总体而言,ASM提供了最底层的字节码操作能力,但使用起来较为复杂;Byte Buddy和Javassist则提供了更高级别、更易用的API,适合大多数常见的字节码增强需求。

        作者组内选用的是Byte Buddy

三、实现

        这里我们需要实现捕捉一个指定路径的类方法,把这个方法的参数、返回值、异常按照一定格式记录下来

1、pom

<dependency>
                <groupId>net.bytebuddy</groupId>
                <artifactId>byte-buddy</artifactId>
                <version>1.14.9</version>
            </dependency>
            <dependency>
                <groupId>net.bytebuddy</groupId>
                <artifactId>byte-buddy-agent</artifactId>
                <version>1.14.9</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <scope>provided</scope>
                <version>3.1.0</version>
            </dependency>

2、脚本

        本质上就是把这个agent打包,然后塞到服务tomcat的仓库里面去

        现在都是塞在服务的docker镜像里面,通过CI/CD打包,发布的时候镜像和包都会一起解压,部署在容器里面,本质上还是生成一个linux的进程。

JAVA_OPTS='"$JAVA_OPTS -javaagent:/

3、agent

        比如需要步骤类的路径是com.ct.InvoiceClass,方法是execute

        创建AgentBuilder对象,并使用ElementMatchers来匹配类名以`com.ct.InvoiceClass`开头的类

        使用`transform`方法来定义对匹配的类进行转换的逻辑

        使用Advice.to方法将`InvoiceClassInterceptor`类中的方法应用到`execute`方法上。

import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;

public class InvoiceClassAgent {

    public static void premain(String agentArgs, Instrumentation inst) {
        new AgentBuilder.Default()
                .type(ElementMatchers.nameStartsWith("com.ct.InvoiceClass"))
                .transform((builder, typeDescription, classLoader, module) ->
                        builder.visit(Advice.to(InvoiceClassInterceptor.class).on(ElementMatchers.named("execute")))
                )
                .installOn(inst);
    }

    public static class InvoiceClassInterceptor {
        @Advice.OnMethodEnter
        public static void enter(@Advice.Argument(0) Object arg) {
            //进入方法之前的处理
        }

        // @Advice.OnMethodExit表示在目标方法执行后执行
        // @Advice.Return注解来获取目标方法的返回值
        // Object[] allArguments就是拿到所有的入参
        @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
    public static void OnMethodExit(
        @Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] allArguments,
        @Advice.Thrown Throwable throwable, @Advice.Return Object returnValue) {
        // 进入方法之后的处理
    }
    }
}

4、过滤

        在使用的过程中还要减少扫描,一方面是性能优化,很多基础库扫描没有意义,另外一方面是有些情况下不想进行捕捉,比如日志对象过大

        那就需要基于ElementMatcher.Junction.AbstractBase<TypeDescription>进行过滤,通过实际路径名称进行匹配,匹配上了返回true代表需要过滤

        需要在创建AgentBuilder的时候添加agentBuilder.ignore(new IgnoredTypesMatcher());

public class IgnoredTypesMatcher extends ElementMatcher.Junction.AbstractBase<TypeDescription> {

    private static final String[] IGNORED_STARTS_WITH_NAME =
        new String[] {"net.bytebuddy.", "sun.reflect."};

    private static final String[] IGNORED_CONTAINS_NAME = new String[] {"javassist.", ".asm.", ".reflectasm."};

    @Override
    public boolean matches(TypeDescription target) {
        // isSynthetic用于判断目标类型是否是合成类型
        // 合成类型是由编译器生成的、在源代码中不存在的类型,例如匿名类、内部类、Lambda表达式等。
        if (target.isSynthetic()) {
            return true;
        }
        String name = target.getActualName();

        for (String ignored : IGNORED_STARTS_WITH_NAME) {
            if (name.startsWith(ignored)) {
                return true;
            }
        }

        for (String ignored : IGNORED_CONTAINS_NAME) {
            if (name.contains(ignored)) {
                return true;
            }
        }

        return false;
    }
}

 5、打包

        agent最终都是要打包的,然后不断被系统镜像拉取对应路径下的包,如果是本地测试的话就要在idea里面设置-javaagent:/***-agent-1.0.0-SNAPSHOT.jar

四、总结

       在使用agent之前,组内也尝试使用父pom,但是由于升级的时候需要改版本发布,几十个系统每次都让组内痛不欲生,最后做了agent,通过CI/CD进行部署,之后有升级只需要重建就行了。

        有使用的同学欢迎评论区交流!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BLEU: a Method for Automatic Evaluation of Machine Translation

文章目录 BLEU: a Method for Automatic Evaluation of Machine Translation背景和意义技术原理考虑 n n n - gram中 n 1 n1 n1 的情况考虑 n n n - gram中 n > 1 n\gt 1 n>1 的情况考虑在文本中的评估初步实验评估和结论统一不同 n n n 值下的评估数值考虑句子长度…

Ubuntu上Jenkins自动化部署Gitee上SpringBoot项目

文章目录 安装安装JDK安装Maven安装GitNodeJS安装&#xff08;可选&#xff09;安装Jenkins 配置Jenkins为Jenkins更换插件源设置jenkins时区安装插件全局工具配置添加Gitee凭证Gitee项目配置 部署后端1.新建任务2.配置源码管理3.构建触发器4.到Gitee中添加WebHook5.构建环境6.…

C++——基础语法(3):内联函数、auto关键字、基于范围的for循环、空指针nullptr

6. 内联函数 在函数前加入inline修饰即可将函数变为内联函数。所谓内联函数&#xff0c;就是在编译时C编译器会将函数体在调用内联函数的地方展开&#xff0c;从而省去了调用函数的栈帧开销&#xff0c;提高程序运行效率。 inline int Add(int a, int b) {return a b; } int …

十一、计算机视觉-膨胀操作

文章目录 前言一、什么是膨胀二、膨胀操作的实现1.引入库 三、膨胀的原理 前言 上节我们学习了腐蚀操作&#xff0c;本节我们讲一下膨胀操作&#xff0c;膨胀和腐蚀实际上是相反的操作。上节我们把云峰这2个字周围没用的像素去掉了&#xff0c;但是云峰这2个字也变细了&#x…

Protocol Buffer-nanopb介绍

文章目录 一、需求二、环境三、相关概念3.1 protocol buffer介绍3.2 nanopb&#xff08;支持C语言&#xff09;3.3 proto文件 四、proto基本语法4.1 proto文件的定义4.2 字段规则4.3 字段类型4.4 字段编号4.5 proto语法4.6 进阶语法4.6.1 message嵌套4.6.2 enum关键字4.6.3 one…

【Flink精讲】Flink状态及Checkpoint调优

RocksDB大状态调优 RocksDB 是基于 LSM Tree 实现的&#xff08;类似 HBase&#xff09; &#xff0c;写数据都是先缓存到内存中&#xff0c; 所以 RocksDB 的写请求效率比较高。 RocksDB 使用内存结合磁盘的方式来存储数据&#xff0c;每 次获取数据时&#xff0c;先从内存中 …

什么是高可用架构

一、什么是高可用 在运维中&#xff0c;经常听到高可用&#xff0c;那么什么是高可用架构呢&#xff1f;通俗点讲&#xff0c;高可用就是在服务故障&#xff0c;节点宕机的情况下&#xff0c;业务能够保证不中断&#xff0c;服务正常运行。 举个例子&#xff0c;支付宝&#…

GS069——直流有刷电机调速电路 通过外接电阻网络,改变与之相接的 VMOS 管的输出,达到控制电动工具 转速的作用。 功耗小,电源电压范围宽。

GS069电动工具直流调速电路是CMOS专用集成电路&#xff0c;具有电源电压范 围宽、功耗小、抗干扰能力强等特点。通过外接电阻网络&#xff0c;改变与之相接 的VMOS 管的输出&#xff0c;达到控制电动工具转速的作用。该电路输出幅值宽&#xff0c; 频率变化小&#xff0c;占空比…

vue ts html 中如何遍历 Enum 类型构建页面结构

vue ts html 中如何遍历 Enum 类型构建页面结构 一、需求 定义了一个 Enum 用来标记菜单类型&#xff1a; enum EnumMenuType {目录 1,菜单,按钮,外链 }你得 Enum 知道它的序号是随第一个定义的值自动增长的 现在想在 ElementUI 界面的 radio-group 中遍历它&#xff0c;…

聚集高速托盘类四向穿梭车ASRV|一车跑全仓可获得10000个货位的HEGERLS智能搬运机器人

随着国内外制造业加速转型升级&#xff0c;越来越多的企业需要进行物流智能化升级&#xff0c;但是往往受到仓库面积、高度、形状等现实条件的限制&#xff0c;以及市场不确定性因素的影响。因此&#xff0c;相对于投资传统的自动化立体库&#xff0c;企业更倾向于选择智能化、…

HarmonyOS—低代码开发Demo示例

接下来为大家展示一个低代码开发的JS工程的Demo示例&#xff0c;使用低代码开发如下华为手机介绍列表的HarmonyOS应用/服务示例。 1.删除模板页面中的控件后&#xff0c;选中组件栏中的List组件&#xff0c;将其拖至中央画布区域&#xff0c;松开鼠标&#xff0c;实现一个List组…

[设计模式Java实现附plantuml源码~行为型] 撤销功能的实现——备忘录模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

formality:set_constant应用

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 往期文章链接: formality:形式验证流程 scan mode func的功能检查需要把scan mode设置成0。

python Matplotlib Tkinter-->导出pdf报表

环境 python:python-3.12.0-amd64 包: matplotlib 3.8.2 reportlab 4.0.9 import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import tkinter as tk import tkinter.messagebox as messagebox impor…

逆变器专题(9)-光伏并网逆变器PQ控制

相应仿真原件请移步资源下载 原理 跟网型并网逆变器通常采用PQ功率环并网&#xff0c;采用功率环并网通常可以进行设计其功率因数 如图所示&#xff0c;通过控制有功功率、无功功率的值&#xff0c;即可实现对功率因数的控制&#xff0c;通常情况下在并网时可将无功功率控为0&…

虚拟机JVM

虚拟机 1、定义jvm 假想计算机 运行在操作系统之上 和硬件之间没有直接交互 包括 一套字节码指令、寄存器、栈、垃圾回收、堆 一个存储方法域 jvm:承担一个翻译工作&#xff0c;动态的将java代码编译成操作系统可以识别的机器码。 从软件层面屏蔽了不同操作系统在底层硬件与指…

js实现鼠标拖拽改变div大小的同时另一个div宽度也变化

实现效果如下图所示 源码如下 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>.box {width: 100%;height: 300px; display: flex;}/*左侧div样式*/.left {width: calc(30% - 5px); /*左侧初始…

naive-ui-admin BasicTable 列表操作栏显示图标icon

效果图 在使用BasicTable的页面添加引用&#xff0c;这里随便弄了个icon import { GameController } from "vicons/ionicons5" 自定义列 const actionColumn reactive({width: 180,title: "操作",key: "action",fixed: "right",ren…

【高德地图】Android高德地图控件交互详细介绍

&#x1f4d6;第5章 与地图控件交互 ✅控件交互&#x1f9ca;缩放按钮&#x1f9ca;指南针&#x1f9ca;定位按钮&#x1f9ca;地图Logo ✅手势交互&#x1f9ca;缩放手势&#x1f9ca;滑动手势&#x1f9ca;旋转手势&#x1f9ca;倾斜手势&#x1f9ca;指定屏幕中心点的手势操…