字节码Javassist如何给反射提速

01 引言

在业务项目中经常会看到Javassist的依赖,框架升级Javassist也得跟着升,否则启动不起来。

很多微服务框架底层必然使用了反射技术,而为了提高性能,就是使用Javassist提速的。但是这个Javassist到时是什么呢?既然比反射的性能好,为什么不直接弃用反射,直接使用Javassist呢?带着这些疑问,特意学习了相关技术,如有问题还请包涵。

02 场景

比如使用JDBC查出ResultSet这样的结果集,我们需要将结果封装到一个JavaBean中,而这个JavaBean只有类名,我们将如何处理呢。因为只有类名,我们无法直接实例化,第一印象想到的就是反射。

2.1 JavaBean

@Data
public class UserInfo {

    private Integer id;
    private String name;

    public void test(){
		 System.out.println("test方法执行....name=" + name);
    }
}

我们假设从数据库取出name值,最后调用test()方法,将name打印出来。

2.2 反射案例

@Test
void test01() throws Exception {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for (int i = 0; i < 1000000; i++) {
        invokeTest(i, UserInfo.class);
    }

    stopWatch.stop();
    System.out.println("耗时:" + stopWatch.getTotalTimeMillis() + "ms");
}

为了对比反射和提速的效果,我们将数据量设为1000000

反射的方法比较简单:

private void invokeTest(Integer index, Class<?> clazz) throws Exception {
    Object o = clazz.getDeclaredConstructor().newInstance();

    Field field = clazz.getDeclaredField("name");
    field.setAccessible(true);
    field.set(o, "test" + index);

    Method method = clazz.getMethod("test");
    method.invoke(o);
}

直接实例化后,赋值打印即可。

100W的数据耗时:14s

03 Javassist加速

javassist依赖有很多,注意甄别。

我们项目使用的是第一个:

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.30.2-GA</version>
</dependency>

反射的性能不好,所以通过反射调用方法就会比较耗时。那我们就利用javassist的特性,直接通过new的方法实例化后调用方法就会变快了。

3.1 构建代理类

构建代理类之前,我们需要创建代理类的父类,用来接收代理类:

public abstract class BeanProxy {

    public abstract void invoke(Integer index);
}

创建实际的代理类:

private BeanProxy getBeanProxy(Class<?> clazz) throws Exception {

    ClassPool pool = ClassPool.getDefault();
    
    // 创建代理类
    CtClass ccProxy = pool.makeClass(clazz.getName() + "Proxy", pool.makeClass(BeanProxy.class.getName()));

    // 创建默认构造器
    CtConstructor ctConstructor = new CtConstructor(new CtClass[0], ccProxy);
    ctConstructor.setBody("{}");
    ccProxy.addConstructor(ctConstructor);

    // 调用目标类的方法
    String methodStr = """
            public void invoke(Integer index){
                %s userInfo = new %s();
                userInfo.setName(\"%s\" + index);
                userInfo.test();
            }
            """;
    methodStr = String.format(methodStr, clazz.getName(), clazz.getName(), "test");
    CtMethod method = CtNewMethod.make(methodStr, ccProxy);
    // 添加代理方法
    ccProxy.addMethod(method);

    // 返回代理对象                            
    return (BeanProxy)ccProxy.toClass().getDeclaredConstructor().newInstance();
}

3.2 重点说明

实际的代理类动态状态分别包含了两部分:

  • 构造器:必须要有,没有就会报错
  • 方法:目标方法

在目标方法中,使用了new的方式,来构建JavaBean,较少了反射带来的损耗。最后通过反射返回代理类,后续的调用就是正常的Java调用了。简单来说,就是反射只用了一次。

3.3 效果

100W的数据,实例化只用了5s左右。提速只是Javassist的冰山一角,还可以对原有的类增强。

04 Javassist

4.1 简介

Javassist是一个可以在运行时动态修改Java字节码的库。它允许程序在运行时生成、修改或分析Java类,而不需要直接操作复杂的字节码指令。

Javassist的核心类:

  • ClassPool:一个CtClass对象的容器,用于管理类。可以从ClassPool中获取CtClass对象。
  • CtClass:表示一个类,可以通过它来修改类。
  • CtMethod:表示类中的方法,可以修改方法体。
  • CtField:表示类中的字段。
  • CtConstructor:表示构造函数。

4.2 字节码增强

案例中的UserInfo有一个test方法,我们需要在方法执行券后,分别增强。

 @Test
void test04() throws Exception {
    ClassPool pool = ClassPool.getDefault();
    CtClass cc = pool.get("com.simonking.boot.tx.pojo.UserInfo");
    CtMethod m = cc.getDeclaredMethod("test");

    m.insertBefore("""
             { 
                System.out.println(\"Before test\");
             }
            """);
    m.insertAfter("""
             { 
                System.out.println(\"after test\");
             }
            """);
    UserInfo u = (UserInfo)cc.toClass().getDeclaredConstructor().newInstance();
    u.test();
}
}

执行效果:

4.3 热修复

@Test
void test05() throws Exception {
    ClassPool pool = ClassPool.getDefault();
    CtClass cc = pool.get("com.simonking.boot.tx.pojo.UserInfo");
    CtMethod m = cc.getDeclaredMethod("test");
    m.setBody("""
            {
                System.out.println(\"test方法异常被修复了....\");
            }
            """);

    UserInfo u = (UserInfo)cc.toClass().getDeclaredConstructor().newInstance();
    u.test();
}

我们可以看到这里直接修改原来的方法。

05 小结

Javassist的适用场景远不止这些,后面发现有趣的场景,再继续分享。在测试过程中发现有两个问题记录一下。

5.1 问题1

Javassist针对已经存在的属性赋值时,如果是包装类可能存在异常:

因为类的属性是Integer类型,导致无法赋值。改为基础数据类型后正常了。

5.2 问题2

Java17编译的时候,会出现问题:

启动参数增加配置可以解决:

--add-opens java.base/java.lang=ALL-UNNAMED
内容概要:本文出自罗兰贝格关于工业4.0现状的报告,系统分析了制造业在数字化转型过程中的实际进展与挑战。报告指出,尽管“工业4.0”概念提出已逾十年,但多数企业仍未实现预期的智能化、自组织生产目标,主要受限于技术复杂性、组织孤岛、投资回报周期长及人才短缺等问题。通过对领先制造企业的研究,报告提炼出三大成功要素:一是制定基于现实的工业4.0愿景与全面战略,明确用例优先级;二是建立“中心辐射式”组织架构,设立专职数字化制造部门,推动跨职能协作与规模化落地;三是构建统一的IT/OT目标架构,强化数据生态与系统互操作性。报告特别强调,高价值用例如预测性维护、实时参数优化、视觉检测等已在汽车与半导体行业显现显著成效,企业应聚焦可量化回报的场景,结合资源现实,分阶段推进转型。; 适合人群:制造业企业管理者、数字化转型负责人、工业互联网从业者及政策制定者; 使用场景及目标:①帮助企业评估自身工业4.0成熟度并制定务实发展战略;②为制造企业设计组织架构与IT/OT技术路线图提供参考;③指导资源优先配置于高价值数字化用例,提升投资回报率; 阅读建议:建议结合企业实际生产场景阅读,重点关注“中心辐射式”运营模式与六大高价值用例的适用性分析,同时参考报告中的汽车行业案例,因地制宜地规划数字化路径。
内容概要:本文围绕基于蚁狮优化算法(ALO)在复杂三维动态环境下求解多无人机动态避障路径规划问题展开研究,并提供了完整的Matlab代码实现。该研究旨在解决多无人机系统在存在障碍物和动态变化环境中的高效、安全路径规划挑战,通过引入ALO算法优化飞行轨迹,有效规避障碍并实现路径最优。研究不仅关注算法层面的实现,还涵盖了目标函数设计、约束条件处理、环境建模等关键技术环节,确保路径规划结果兼具可行性与鲁棒性。此外,文档附带丰富的相关科研资源,涵盖路径规划、智能优化算法、机器学习、电力系统等多个领域,为后续拓展研究提供坚实支撑。; 适合人群:具备一定编程基础,熟悉Matlab工具,从事无人机路径规划、智能优化算法或智能系统研究的科研人员及研究生。; 使用场景及目标:①研究复杂三维动态环境下多无人机的协同避障路径规划问题;②掌握蚁狮优化算法(ALO)在路径规划中的应用与实现机制;③为智能交通、无人系统控制、自动化调度等相关课题提供算法参考与代码支持; 阅读建议:建议结合Matlab代码深入理解ALO算法的具体实现流程,重点关注目标函数构建、动态障碍建模与避障策略设计等关键模块,同时可参照文中提及的其他智能优化算法(如PSO、GWO等)进行对比实验,进一步提升算法性能分析与工程应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智_永无止境

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值