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

news2024/10/2 8:19:53

一、引言

        一般如果需要做增强类的架构工具会使用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进行部署,之后有升级只需要重建就行了。

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

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

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

相关文章

数独游戏(dfs)

代码注释如下 #include <iostream> using namespace std; const int N 10; bool col[N][N], rol[N][N], cell[3][3][N]; char g[N][N]; bool dfs(int x, int y) { //用bool这样在找到一个方案就可以迅速退出if(y 9) x, y 0; //若y超出边界&#xff0c;则第二…

李沐动手学习深度学习——3.6练习

本节直接实现了基于数学定义softmax运算的softmax函数。这可能会导致什么问题&#xff1f;提示&#xff1a;尝试计算exp(50)的大小。 可能存在超过计算机最大64位的存储&#xff0c;导致精度溢出&#xff0c;影响最终计算结果。 本节中的函数cross_entropy是根据交叉熵损失函数…

人工智能指数报告2023

人工智能指数报告2023 主要要点第 1 章 研究与开发第 2 章 技术性能第 3 章 人工智能技术伦理第 4 章 经济第 5 章 教育第 6 章 政策与治理第 7 章 多样性第 8 章 舆论 人工智能指数是斯坦福大学以人为本的人工智能研究所&#xff08;HAI&#xff09;的一项独立倡议&#xff0c…

基于 LLaMA 和 LangChain 实践本地 AI 知识库

有时候,我难免不由地感慨,真实的人类世界,本就是一个巨大的娱乐圈,即使是在英雄辈出的 IT 行业。数日前,Google 正式对外发布了 Gemini 1.5 Pro,一个建立在 Transformer 和 MoE 架构上的多模态模型。可惜,这个被 Google 寄予厚望的产品并未激起多少水花,因为就在同一天…

STM32 中断流程介绍

STM32可以产生中断的事件多种多样&#xff0c;比如&#xff1a;定时器时间结束、串口接收到数据、某个GPIO检测到电平变化等等等等。 1、STM32 gpio 中断处理流程介绍 1、从引脚进入的高低电平首先由输入驱动器处理&#xff0c;如下图 2、经过输入驱动器处理后的信号会进…

BUUCTF---[极客大挑战 2019]LoveSQL1

1.题目描述 2.一般遇到登录&#xff0c;用户名会尝试admin&#xff0c;密码会尝试万能密码" or 11#或者 or 11#。这里尝试了第一种&#xff0c;但是不对。 3.接着尝试第二种 or 11#。提示登录成功了说明这里是单引号注入&#xff0c;并给了一串字符 4.sql注入题一般会设计…

获取linuxIP、内存、cpu、磁盘IO等信息的Shell脚本及其讲解

shell基础知识 1.grep grep是一个在Unix和Unix-like系统上使用的命令行工具&#xff0c;用于在文本文件中搜索匹配指定模式的行。它的名字来自于"global regular expression print"&#xff08;全局正则表达式打印&#xff09;的缩写。grep的基本用法是通过指定一个…

神经网络之万能定理python-pytorch实现,可以拟合任意曲线

神经网络之万能定理python-pytorch实现&#xff0c;可以拟合任意曲线 博主&#xff0c;这几天一直在做这个曲线拟合的实验&#xff0c;讲道理&#xff0c;网上可能也有很多这方面的资料&#xff0c;但是博主其实试了很多&#xff0c;效果只能对一般的曲线还行&#xff0c;稍微…

设计模式——中介者模式(mediator pattern)

概述 如果在一个系统中对象之间的联系呈现为网状结构&#xff0c;如下图所示。对象之间存在大量的多对多联系&#xff0c;将导致系统非常复杂&#xff0c;这些对象既会影响别的对象&#xff0c;也会被别的对象所影响&#xff0c;这些对象称为同事对象&#xff0c;它们之间通过彼…

智慧市容环境卫生管理信息系统建设项目初步设计参考指南

第四章项目建设方案 梳理和编制数据标准规范&#xff0c;为数据体系建设提供建设指导。数据标准规范体系是根据统一市容环卫基础数据资源建立的&#xff0c;从要素分类、编码、符号、制图、更新机制等层 面解决各类规划标准不衔接、各自为政问题。标准规范体系包括&#xff1…

数据挖掘入门项目二手交易车价格预测之数据分析

文章目录 1. 相关库的引入2. 数据的加载3. 数据概况3.1 统计值查看3.2 查看数据类型 4. 判断缺失值4.1 统计每一列空值的数量4.2 可视化缺失值数量 5. 判断异常值5.1 异常值检测 6. 了解预测值的分布6.1 统计各预测值的分布6.2 总体分布概况6.2 查看预测值的具体频数6.3 查看sk…

HTTPS的实现原理

图片来源&#xff1a;HTTPS 详解一&#xff1a;附带最精美详尽的 HTTPS 原理图 - 个人文章 - SegmentFault 思否 加密流程按图中的序号分为&#xff1a; 客户端请求 HTTPS 网址&#xff0c;然后连接到 server 的 443 端口 (HTTPS 默认端口&#xff0c;类似于 HTTP 的80端口)。…

双周回顾#006 - 这三个月

断更啦~~ 上次更新时间 2023/11/23, 断更近三个月的时间。 先狡辩下&#xff0c;因为忙、着实忙。因为忙&#xff0c;心安理得给断更找了个借口&#xff0c;批评下自己~~ 这三个月在做啥&#xff1f;跨部门援助&#xff0c;支援公司互联网的 ToC 项目&#xff0c;一言难尽。 …

【随记】分享第1期(2024.03.02)

记录这段时间&#xff0c;看到的有趣/有用/值得分享的东西 灵感来源&#xff1a;分类&#xff1a;周刊 - 阮一峰的网络日志 (ruanyifeng.com) 文章目录 大佬博客实用工具文章文摘 大佬博客 云风的 BLOG (codingnow.com) 美团技术团队 (meituan.com) 计算机科学 – 刘未鹏 | Mi…

可以用来测试的接口

实际开发过程中&#xff0c;我们可以通过postman工具来测试接口 get请求 https://api.github.com/events?id1&nameuser post请求 http://httpbin.org/post 参数1&#xff1a;key1value1 参数2&#xff1a;key2value2

springboot238光影视频

光影视频平台 摘 要 使用旧方法对光影视频平台的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在光影视频平台的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开…

第二讲:用geth和以太坊交互

一&#xff1a;安装geth brew install ethereum geth github网址&#xff1a; https://github.com/ethereum/go-ethereum 二&#xff1a; 用geth连接以太坊 以太坊有主网络&#xff08;Ethereum Mainnet&#xff09;&#xff0c;有测试网络&#xff08;Sepolia、Goerli 等等…

打基础!张宇《30讲》vs 武忠祥《基础篇》

张宇老师和武忠祥老师的课程都很推荐&#xff0c;两个老师也都很有实力 我在考研的时候跟的是张宇老师&#xff0c;然后强化阶段跟的是武忠祥老师&#xff0c;我真实的听过两个老师的课程&#xff0c;所以我觉得我有一些发言权。因此对大家在考研数学备考选择老师方面&#xf…

单细胞Seurat - 降维与细胞标记(4)

本系列持续更新Seurat单细胞分析教程&#xff0c;欢迎关注&#xff01; 非线形降维 Seurat 提供了几种非线性降维技术&#xff0c;例如 tSNE 和 UMAP&#xff0c;来可视化和探索这些数据集。这些算法的目标是学习数据集中的底层结构&#xff0c;以便将相似的细胞放在低维空间中…

JavaScript 设计模式之职责链模式

职责链 在日常开发中&#xff0c;我们一个函数&#xff08;方法&#xff09;应该是尽可能的单单只做一件事&#xff0c;比如 一个获取 name 的函数&#xff0c;他就用来返回 name 处理 name相关的数据就好了&#xff0c;这就是职责 function getName(){// todo sth.return na…