如何使用java agent修改字节码并在springboot启动时自动生效

 更新时间:2024年10月26日 14:07:09   作者:gyx_ruarua  
本文介绍了JavaAgent的使用方法和在SpringBoot中的应用,JavaAgent可以通过修改类的字节码,实现对非Spring容器管理对象的AOP处理,演示了如何定义切面逻辑,实现接口mock,感兴趣的朋友跟随小编一起看看吧

       在java开发的过程中,我们经常遇到一些需要对某个方法做切面的需求,通常做法是使用spring提供的AOP功能,对方法做切面,例如方法出入参打印,接口mock等等。但很多时候,需要切面的方法是非spring容器管理的类,例如需要对okhttp、apacheHttpClient请求做mock,判断http请求的参数、url为某个值时,返回测试结果,例如请求参数中包含userName=tester,返回code=200。这种使用可以使用java Agent,通过java agent修改类的字节码,实现对非spring容器管理对象的aop处理。但是使用javaAgent后,启动时需要添加参数-javaagent:xxx.jar,使用方还需要下载对象的jar到本地,使用起来略显麻烦。proxy-sdk就是为了解决这个问题,通过maven、gradle依赖工具直接引入jar依赖即可,然后添加你的切面逻辑,springboot服务启动即可生效。

GitHub - YingXinGuo95/proxy-agent: java agent with springboot

按照github上的README的指引我们引入jar包,从maven的中央仓库下载依赖。

<dependency>
  <groupId>io.github.yingxinguo95</groupId>
  <artifactId>proxy-sdk</artifactId>
  <version>0.0.1</version>
  <!-- 0.0.1拉取不到可以试试0.0.1-RELEASE -->
  <!--<version>0.0.1-RELEASE</version>-->
</dependency>

配置需要代理方法配置和定义我们自己的需要重写逻辑,例如我们需要apache httpclient的请求方法,实现接口mock,判断当请求某个地址时返回测试数据,而不是真的请求对方。

 例如以下代码,前置重写httpClient的execute方法,当请求baidu.com时就返回测试的json数据{\"code\":\"200\", \"msg\":\"mock data\"}

import io.github.proxy.annotation.ProxyRecodeCfg;
import io.github.proxy.annotation.ReCodeType;
import io.github.proxy.service.ProxyReCode;
import lombok.SneakyThrows;
import org.apache.http.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
/**
 * 重写apacheHttp请求处理逻辑,实现mock功能
 */
@Component
public class MockApacheHttpReCoder implements ProxyReCode {
    @SneakyThrows
    @ProxyRecodeCfg(proxyClassName="org.apache.http.impl.client.CloseableHttpClient", method="execute", type = ReCodeType.BEFORE)
    public static CloseableHttpResponse executeProxy1(HttpUriRequest request, HttpContext context) {
        String path = request.getURI().toString();
        if (request instanceof HttpEntityEnclosingRequestBase) {
            //post请求读取body
            HttpEntity entity = ((HttpEntityEnclosingRequestBase)request).getEntity();
            if (entity == null) {
                return null;
            }
            String reqBody = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        }
        if (path.startsWith("http://baidu.com")) {
            return buildMockResponse("{\"code\":\"200\", \"msg\":\"mock data\"}", null);
        }
        return null;
    }
    @SneakyThrows
    @ProxyRecodeCfg(proxyClassName="org.apache.http.impl.client.CloseableHttpClient", method="execute", type = ReCodeType.BEFORE)
    public static CloseableHttpResponse executeProxy2(HttpHost target, HttpRequest request, HttpContext context) {
        String path = request.getRequestLine().getUri();
        if (request instanceof HttpEntityEnclosingRequestBase) {
            //post请求读取body
            HttpEntity entity = ((HttpEntityEnclosingRequestBase)request).getEntity();
            if (entity == null) {
                return null;
            }
            String reqBody = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        }
        if (path.startsWith("http://baidu.com")) {
            return buildMockResponse("{\"code\":\"200\", \"msg\":\"mock返回\"}", null);
        }
        return null;
    }
    public static CloseableHttpResponse buildMockResponse(String mockValue, Header[] headers) {
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        String reasonPhrase = "OK";
        StatusLine statusline = new BasicStatusLine(protocolVersion, HttpStatus.SC_OK, reasonPhrase);
        MockCloseableHttpResponse mockResponse = new MockCloseableHttpResponse(statusline);
        BasicHttpEntity entity = new BasicHttpEntity();
        InputStream inputStream = new ByteArrayInputStream(mockValue.getBytes());
        entity.setContent(inputStream);
        entity.setContentLength(mockValue.length());
        entity.setChunked(false);
        mockResponse.setEntity(entity);
        if (headers != null) {
            mockResponse.setHeaders(headers);
        }
        return mockResponse;
    }
    public static class MockCloseableHttpResponse extends BasicHttpResponse implements CloseableHttpResponse {
        public MockCloseableHttpResponse(StatusLine statusline, ReasonPhraseCatalog catalog, Locale locale) {
            super(statusline, catalog, locale);
        }
        public MockCloseableHttpResponse(StatusLine statusline) {
            super(statusline);
        }
        public MockCloseableHttpResponse(ProtocolVersion ver, int code, String reason) {
            super(ver, code, reason);
        }
        @Override
        public void close() throws IOException {
        }
    }
}

我们在springBoot启动后请求试试

@SpringBootApplication
public class AppMain {
    @SneakyThrows
    public static void main(String[] args) {
        SpringApplication.run(AppMain.class, args);
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建请求对象
        HttpGet httpGet = new HttpGet("http://baidu.com");
        //发送请求,请求响应结果
        CloseableHttpResponse response = httpClient.execute(httpGet);
        //获取服务器返回的状态码
        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println(">>>>>>>>>服务端返回成功的状态码为:"+statusCode);
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity);
        System.out.println(">>>>>>>>>服务端返回的数据为:"+body);
        //关闭资源
        response.close();
        httpClient.close();
    }
}

启动服务,在控制台可以看到打印了对org.apache.http.impl.client.CloseableHttpClient类做字节码重写

[Attach Listener] INFO  i.g.p.transformer.ReCoderTransformer -[proxy-agent] rewrite class:[org.apache.http.impl.client.CloseableHttpClient]
[Attach Listener] INFO  io.github.proxy.AgentMain -[proxy-agent] redefine loaded class complete, cost:171ms

springboot启动后请求baidu.com,得到结果,顺利得到了我们需要的测试数据

我们调整下代码,new HttpGet("http://zhihu.com"),换一个请求地址,然后发起请求

执行了httpClient的原始逻辑,请求zhihu.com拿到了响应。

简单试验就到这里了,其他用法可以按照github上readme指引试验一下。觉的这个小工具不错小伙伴可以点个star~

到此这篇关于使用java agent修改字节码,并在springboot启动时自动生效的文章就介绍到这了,更多相关java agent修改字节码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Jackson的用法实例分析

    Jackson的用法实例分析

    这篇文章主要介绍了Jackson的用法实例分析,用于处理Java的json格式数据非常实用,需要的朋友可以参考下
    2014-08-08
  • 浅谈Java自动装箱与拆箱及其陷阱

    浅谈Java自动装箱与拆箱及其陷阱

    下面小编就为大家带来一篇浅谈Java自动装箱与拆箱及其陷阱。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • Feign Client 超时时间配置不生效的解决

    Feign Client 超时时间配置不生效的解决

    这篇文章主要介绍了Feign Client 超时时间配置不生效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java微信公众号安全模式消息解密

    Java微信公众号安全模式消息解密

    这篇文章主要为大家详细介绍了Java微信公众号安全模式消息解密,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • 利用SpringBoot解决多个定时任务阻塞的问题

    利用SpringBoot解决多个定时任务阻塞的问题

    当我们在Spring Boot应用中使用多个定时任务时,任务之间的阻塞可能是一个常见的问题,这可能会因任务之间的依赖、执行时间过长或资源争用等原因而发生,本文让我们深入探讨如何利用Spring Boot来解决多个定时任务阻塞的问题,感兴趣的小伙伴跟着小编一起来看看吧
    2024-01-01
  • Springboot系列之kafka操作使用详解

    Springboot系列之kafka操作使用详解

    这篇文章主要为大家介绍了Springboot系列之kafka操作使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • 实例讲解Java的Spring框架中的控制反转和依赖注入

    实例讲解Java的Spring框架中的控制反转和依赖注入

    这篇文章主要介绍了Java的Spring框架中的控制反转和依赖注入,Spring是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2016-02-02
  • 配置Ant执行Jmeter脚本过程详解

    配置Ant执行Jmeter脚本过程详解

    这篇文章主要介绍了配置Ant执行Jmeter脚本过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • spring事务里面开启线程插入报错了是否会回滚

    spring事务里面开启线程插入报错了是否会回滚

    这篇文章主要介绍了spring事务里面开启线程插入,报错了是否会回滚?这是小编遇到一道面试题,题目大概是这个样子,今天抽空通过示例代码给大家分析下,需要的朋友可以参考下
    2023-04-04
  • Java 超详细带你掌握矩阵的运算

    Java 超详细带你掌握矩阵的运算

    在学习机器学习算法时,发现运用java 来实现有些算法代码时,会有很大困难,其中有一点就是 java 本身并没有矩阵运算的 api,所以进行要实现矩阵运算就尤其复杂,让我们一起了解矩阵的运算
    2022-03-03

最新评论