Javassist实战指南

Javassist实战指南:Java字节码操作从入门到精通

一、Javassist简介

1.1 什么是Javassist

Javassist(Java Programming Assistant)是一个强大的Java字节码操作库,它允许开发者在运行时动态修改Java类和生成新的Java类。与ASM等底层字节码框架相比,Javassist提供了更高级、更易用的API,使得字节码操作变得简单直观。

Javassist的核心特点:

  • 🎯 源代码级API:可以使用Java源代码字符串来操作字节码
  • 🚀 简单易用:无需深入了解JVM字节码指令
  • 💡 功能强大:支持类创建、修改、方法拦截等
  • 🔧 广泛应用:Hibernate、JBoss、Spring等框架都在使用
  • 性能优秀:运行时开销小

1.2 应用场景

复制代码
Javassist典型应用场景:
┌─────────────────────────────────────────────────────┐
│                   应用领域                           │
├─────────────────────────────────────────────────────┤
│  • AOP编程         - 方法拦截、日志、权限            │
│  • ORM框架         - Hibernate延迟加载、代理         │
│  • Mock测试        - 动态生成测试桩                  │
│  • 热部署          - 运行时类替换                    │
│  • 性能监控        - 方法耗时统计、调用链追踪        │
│  • 代码注入        - 动态添加代码逻辑                │
│  • 反射增强        - 优化反射性能                    │
└─────────────────────────────────────────────────────┘

1.3 Maven依赖

xml 复制代码
<dependencies>
    <!-- Javassist核心库 -->
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.29.2-GA</version>
    </dependency>

    <!-- 日志依赖(可选) -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.9</version>
    </dependency>

    <!-- 测试依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

1.4 Javassist vs 其他字节码框架

复制代码
特性对比:
┌──────────────┬─────────────┬─────────────┬─────────────┐
│    特性      │  Javassist  │   ASM       │  ByteBuddy  │
├──────────────┼─────────────┼─────────────┼─────────────┤
│ 易用性       │ ★★★★★      │ ★★☆☆☆      │ ★★★★☆      │
├──────────────┼─────────────┼─────────────┼─────────────┤
│ 性能         │ ★★★★☆      │ ★★★★★      │ ★★★★☆      │
├──────────────┼─────────────┼─────────────┼─────────────┤
│ 功能丰富度   │ ★★★★☆      │ ★★★★★      │ ★★★★★      │
├──────────────┼─────────────┼─────────────┼─────────────┤
│ 学习曲线     │ 平缓        │ 陡峭        │ 中等        │
├──────────────┼─────────────┼─────────────┼─────────────┤
│ 社区支持     │ ★★★★☆      │ ★★★★★      │ ★★★★☆      │
└──────────────┴─────────────┴─────────────┴─────────────┘

二、Javassist核心API

2.1 ClassPool与CtClass

ClassPool是Javassist的核心类,它是CtClass对象的容器。CtClass代表一个类。

java 复制代码
package com.example.javassist.basic;

import javassist.*;

/**
 * ClassPool和CtClass基础示例
 */
public class ClassPoolDemo {

    public static void main(String[] args) throws Exception {
        // 1. 获取ClassPool实例(类池)
        ClassPool pool = ClassPool.getDefault();

        // 2. 获取已存在的类
        CtClass stringClass = pool.get("java.lang.String");
        System.out.println("类名: " + stringClass.getName());
        System.out.println("父类: " + stringClass.getSuperclass().getName());
        System.out.println("是否接口: " + stringClass.isInterface());

        System.out.println("\n" + "=".repeat(60) + "\n");

        // 3. 创建新类
        CtClass newClass = pool.makeClass("com.example.DynamicClass");
        System.out.println("创建的类: " + newClass.getName());

        // 4. 添加字段
        CtField field = new CtField(CtClass.intType, "age", newClass);
        field.setModifiers(Modifier.PRIVATE);
        newClass.addField(field);

        // 5. 添加方法
        CtMethod getterMethod = CtNewMethod.getter("getAge", field);
        newClass.addMethod(getterMethod);

        CtMethod setterMethod = CtNewMethod.setter("setAge", field);
        newClass.addMethod(setterMethod);

        // 6. 生成Class对象
        Class<?> clazz = newClass.toClass();
        Object instance = clazz.newInstance();

        // 7. 调用生成的方法
        clazz.getMethod("setAge", int.class).invoke(instance, 25);
        int age = (int) clazz.getMethod("getAge").invoke(instance);
        System.out.println("Age: " + age);

        // 8. 打印所有方法
        System.out.println("\n生成的方法列表:");
        for (CtMethod method : newClass.getDeclaredMethods()) {
            System.out.println("  - " + method.getName());
        }
    }
}

输出结果:

makefile 复制代码
类名: java.lang.String
父类: java.lang.Object
是否接口: false

============================================================

创建的类: com.example.DynamicClass
Age: 25

生成的方法列表:
  - getAge
  - setAge

2.2 CtMethod - 方法操作

java 复制代码
package com.example.javassist.basic;

import javassist.*;

/**
 * 方法操作示例
 */
public class MethodDemo {

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 创建类
        CtClass ctClass = pool.makeClass("com.example.Calculator");

        // 1. 创建方法 - 方式1:使用源代码字符串
        CtMethod addMethod = CtNewMethod.make(
            "public int add(int a, int b) { return a + b; }",
            ctClass
        );
        ctClass.addMethod(addMethod);

        // 2. 创建方法 - 方式2:使用API
        CtMethod subtractMethod = new CtMethod(
            CtClass.intType,           // 返回类型
            "subtract",                // 方法名
            new CtClass[]{             // 参数类型
                CtClass.intType,
                CtClass.intType
            },
            ctClass                    // 所属类
        );
        subtractMethod.setModifiers(Modifier.PUBLIC);
        subtractMethod.setBody("{ return $1 - $2; }");  // $1, $2代表参数
        ctClass.addMethod(subtractMethod);

        // 3. 创建复杂方法
        CtMethod multiplyMethod = CtNewMethod.make(
            "public int multiply(int a, int b) {" +
            "    System.out.println(\"计算: \" + a + \" * \" + b);" +
            "    int result = a * b;" +
            "    System.out.println(\"结果: \" + result);" +
            "    return result;" +
            "}",
            ctClass
        );
        ctClass.addMethod(multiplyMethod);

        // 生成类并测试
        Class<?> clazz = ctClass.toClass();
        Object calculator = clazz.newInstance();

        // 测试方法
        int sum = (int) clazz.getMethod("add", int.class, int.class)
            .invoke(calculator, 10, 5);
        System.out.println("10 + 5 = " + sum);

        int diff = (int) clazz.getMethod("subtract", int.class, int.class)
            .invoke(calculator, 10, 5);
        System.out.println("10 - 5 = " + diff);

        int product = (int) clazz.getMethod("multiply", int.class, int.class)
            .invoke(calculator, 10, 5);
        System.out.println("10 * 5 = " + product);
    }
}

2.3 方法拦截 - insertBefore/insertAfter

java 复制代码
package com.example.javassist.basic;

import javassist.*;

/**
 * 方法拦截示例 - 在方法前后插入代码
 */
public class MethodInterceptDemo {

    /**
     * 原始服务类
     */
    public static class UserService {
        public String findUser(Long id) {
            return "User-" + id;
        }

        public void saveUser(String name, int age) {
            System.out.println("  [原方法] 保存用户: " + name + ", 年龄: " + age);
        }

        public int calculateAge(int birthYear) {
            return 2024 - birthYear;
        }
    }

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 获取要修改的类
        CtClass ctClass = pool.get("com.example.javassist.basic.MethodInterceptDemo$UserService");

        // 获取所有方法
        for (CtMethod method : ctClass.getDeclaredMethods()) {
            System.out.println("增强方法: " + method.getName());

            // 在方法开始处插入代码
            method.insertBefore(
                "System.out.println(\"[Before] 调用方法: " + method.getName() + "\");" +
                "System.out.println(\"[Before] 参数: \" + java.util.Arrays.toString($args));" +
                "long startTime = System.currentTimeMillis();"
            );

            // 在方法结束处插入代码
            method.insertAfter(
                "long endTime = System.currentTimeMillis();" +
                "System.out.println(\"[After] 方法: " + method.getName() + " 耗时: \" + (endTime - startTime) + \"ms\");"
            );
        }

        // 加载修改后的类
        Class<?> clazz = ctClass.toClass();
        Object service = clazz.newInstance();

        System.out.println("\n====== 测试增强后的方法 ======\n");

        // 测试1: findUser
        String user = (String) clazz.getMethod("findUser", Long.class)
            .invoke(service, 123L);
        System.out.println("返回值: " + user);

        System.out.println();

        // 测试2: saveUser
        clazz.getMethod("saveUser", String.class, int.class)
            .invoke(service, "张三", 25);

        System.out.println();

        // 测试3: calculateAge
        int age = (int) clazz.getMethod("calculateAge", int.class)
            .invoke(service, 1990);
        System.out.println("返回值: " + age);
    }
}

输出结果:

ini 复制代码
增强方法: findUser
增强方法: saveUser
增强方法: calculateAge

====== 测试增强后的方法 ======

[Before] 调用方法: findUser
[Before] 参数: [123]
[After] 方法: findUser 耗时: 0ms
返回值: User-123

[Before] 调用方法: saveUser
[Before] 参数: [张三, 25]
  [原方法] 保存用户: 张三, 年龄: 25
[After] 方法: saveUser 耗时: 1ms

[Before] 调用方法: calculateAge
[Before] 参数: [1990]
[After] 方法: calculateAge 耗时: 0ms
返回值: 34

三、Javassist特殊变量

3.1 特殊变量详解

Javassist提供了一系列特殊变量,用于在插入的代码中访问方法上下文:

ruby 复制代码
特殊变量说明:
┌────────────┬──────────────────────────────────────┐
│  变量名    │              说明                     │
├────────────┼──────────────────────────────────────┤
│  $0        │  this(仅实例方法)                   │
│  $1, $2... │  方法参数($1表示第一个参数)         │
│  $args     │  所有参数数组(Object[])             │
│  $$        │  所有参数(逗号分隔)                 │
│  $r        │  返回值类型                           │
│  $_        │  返回值(仅在insertAfter中使用)      │
│  $w        │  包装类型转换                         │
│  $sig      │  参数类型数组(Class[])              │
│  $type     │  返回值类型(Class)                  │
│  $class    │  this的类型(Class)                  │
└────────────┴──────────────────────────────────────┘

3.2 特殊变量实战

java 复制代码
package com.example.javassist.advanced;

import javassist.*;

/**
 * Javassist特殊变量使用示例
 */
public class SpecialVariablesDemo {

    public static class DemoService {
        private String name = "DemoService";

        public String processData(String input, int count, boolean flag) {
            return input + "-" + count + "-" + flag;
        }

        public int calculate(int a, int b) {
            return a + b;
        }

        public void voidMethod() {
            System.out.println("  [原方法] voidMethod执行");
        }
    }

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get("com.example.javassist.advanced.SpecialVariablesDemo$DemoService");

        // 1. 演示 $0 (this)
        CtMethod processMethod = ctClass.getDeclaredMethod("processData");
        processMethod.insertBefore(
            "System.out.println(\"[$0] this对象: \" + $0);" +
            "System.out.println(\"[$0] this.name: \" + $0.name);"
        );

        // 2. 演示 $1, $2, $3 (参数)
        processMethod.insertBefore(
            "System.out.println(\"[$1] 第1个参数: \" + $1);" +
            "System.out.println(\"[$2] 第2个参数: \" + $2);" +
            "System.out.println(\"[$3] 第3个参数: \" + $3);"
        );

        // 3. 演示 $args (参数数组)
        processMethod.insertBefore(
            "System.out.println(\"[$args] 参数数组: \" + java.util.Arrays.toString($args));"
        );

        // 4. 演示 $$ (所有参数)
        CtMethod calculateMethod = ctClass.getDeclaredMethod("calculate");
        calculateMethod.insertBefore(
            "System.out.println(\"[$$] 调用原方法,参数: \" + $1 + \", \" + $2);"
        );

        // 5. 演示 $_ (返回值)
        calculateMethod.insertAfter(
            "System.out.println(\"[$_] 原返回值: \" + $_);" +
            "$_ = $_ * 2;" +  // 修改返回值
            "System.out.println(\"[$_] 修改后返回值: \" + $_);"
        );

        // 6. 演示 $r (返回值类型)
        processMethod.insertAfter(
            "if ($_ == null) {" +
            "    $_ = ($r) \"default-value\";" +  // $r进行类型转换
            "}"
        );

        // 7. 演示 $type 和 $class
        processMethod.insertBefore(
            "System.out.println(\"[$type] 返回值类型: \" + $type);" +
            "System.out.println(\"[$class] 当前类: \" + $class);"
        );

        // 8. 演示 $sig (参数类型数组)
        processMethod.insertBefore(
            "System.out.println(\"[$sig] 参数类型: \" + java.util.Arrays.toString($sig));"
        );

        // 加载修改后的类
        Class<?> clazz = ctClass.toClass();
        Object service = clazz.newInstance();

        System.out.println("====== 测试 processData 方法 ======");
        String result = (String) clazz.getMethod("processData", String.class, int.class, boolean.class)
            .invoke(service, "test", 100, true);
        System.out.println("最终返回值: " + result);

        System.out.println("\n====== 测试 calculate 方法 ======");
        int calcResult = (int) clazz.getMethod("calculate", int.class, int.class)
            .invoke(service, 10, 20);
        System.out.println("最终返回值: " + calcResult);
    }
}

四、实战案例:性能监控系统

4.1 方法执行时间统计

java 复制代码
package com.example.javassist.production;

import javassist.*;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 性能监控系统 - 基于Javassist
 */
public class PerformanceMonitor {

    /**
     * 性能指标收集器
     */
    public static class MetricsCollector {
        private static final Map<String, MethodStats> statsMap = new ConcurrentHashMap<>();

        public static class MethodStats {
            AtomicLong count = new AtomicLong(0);
            AtomicLong totalTime = new AtomicLong(0);
            AtomicLong maxTime = new AtomicLong(0);
            AtomicLong minTime = new AtomicLong(Long.MAX_VALUE);
        }

        public static void recordMetric(String methodName, long duration) {
            MethodStats stats = statsMap.computeIfAbsent(methodName, k -> new MethodStats());
            stats.count.incrementAndGet();
            stats.totalTime.addAndGet(duration);
            stats.maxTime.updateAndGet(v -> Math.max(v, duration));
            stats.minTime.updateAndGet(v -> Math.min(v, duration));
        }

        public static void printReport() {
            System.out.println("\n" + "=".repeat(80));
            System.out.println("性能监控报告");
            System.out.println("=".repeat(80));
            System.out.printf("%-40s %10s %12s %10s %10s%n",
                "方法名", "调用次数", "平均耗时(ms)", "最大耗时", "最小耗时");
            System.out.println("-".repeat(80));

            statsMap.forEach((method, stats) -> {
                long avg = stats.count.get() > 0 ?
                    stats.totalTime.get() / stats.count.get() : 0;
                System.out.printf("%-40s %10d %12d %10d %10d%n",
                    method,
                    stats.count.get(),
                    avg,
                    stats.maxTime.get(),
                    stats.minTime.get() == Long.MAX_VALUE ? 0 : stats.minTime.get());
            });
            System.out.println("=".repeat(80));
        }
    }

    /**
     * 增强指定类的所有方法
     */
    public static Class<?> enhance(Class<?> targetClass) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get(targetClass.getName());

        for (CtMethod method : ctClass.getDeclaredMethods()) {
            if (!Modifier.isAbstract(method.getModifiers())) {
                enhanceMethod(method, targetClass.getSimpleName());
            }
        }

        return ctClass.toClass();
    }

    private static void enhanceMethod(CtMethod method, String className) throws CannotCompileException {
        String methodName = className + "." + method.getName();

        // 在方法开始插入计时代码
        method.insertBefore(
            "long __startTime = System.currentTimeMillis();"
        );

        // 在方法结束插入统计代码
        method.insertAfter(
            "long __duration = System.currentTimeMillis() - __startTime;" +
            "com.example.javassist.production.PerformanceMonitor.MetricsCollector.recordMetric(\"" +
            methodName + "\", __duration);"
        );
    }

    /**
     * 模拟业务服务
     */
    public static class OrderService {
        public String createOrder(Long userId, Long productId, int quantity) {
            sleep(50);
            return "ORDER-" + System.currentTimeMillis();
        }

        public void processPayment(String orderId, double amount) {
            sleep(80);
        }

        public String queryOrder(String orderId) {
            sleep(120);
            return "Order Detail: " + orderId;
        }

        public void sendNotification(String orderId) {
            sleep(30);
        }

        private void sleep(long millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 增强OrderService类
        Class<?> enhancedClass = enhance(OrderService.class);
        Object orderService = enhancedClass.newInstance();

        System.out.println("====== 开始性能测试 ======\n");

        // 模拟业务调用
        for (int i = 0; i < 10; i++) {
            enhancedClass.getMethod("createOrder", Long.class, Long.class, int.class)
                .invoke(orderService, 100L + i, 200L + i, 1);
        }

        for (int i = 0; i < 5; i++) {
            enhancedClass.getMethod("processPayment", String.class, double.class)
                .invoke(orderService, "ORDER-" + i, 99.99);
        }

        for (int i = 0; i < 8; i++) {
            enhancedClass.getMethod("queryOrder", String.class)
                .invoke(orderService, "ORDER-" + i);
        }

        for (int i = 0; i < 15; i++) {
            enhancedClass.getMethod("sendNotification", String.class)
                .invoke(orderService, "ORDER-" + i);
        }

        // 打印监控报告
        MetricsCollector.printReport();
    }
}

4.2 异常捕获与日志记录

java 复制代码
package com.example.javassist.production;

import javassist.*;

/**
 * 异常捕获与日志记录
 */
public class ExceptionLogger {

    /**
     * 为方法添加异常捕获
     */
    public static Class<?> addExceptionHandling(Class<?> targetClass) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get(targetClass.getName());

        for (CtMethod method : ctClass.getDeclaredMethods()) {
            if (!Modifier.isAbstract(method.getModifiers())) {
                addTryCatch(method);
            }
        }

        return ctClass.toClass();
    }

    private static void addTryCatch(CtMethod method) throws CannotCompileException {
        // 创建异常处理器
        CtClass exceptionType;
        try {
            exceptionType = ClassPool.getDefault().get("java.lang.Exception");
        } catch (NotFoundException e) {
            throw new CannotCompileException(e);
        }

        // 添加catch块
        method.addCatch(
            "{" +
            "    System.err.println(\"[异常捕获] 方法: " + method.getName() + "\");" +
            "    System.err.println(\"[异常捕获] 异常类型: \" + $e.getClass().getName());" +
            "    System.err.println(\"[异常捕获] 异常信息: \" + $e.getMessage());" +
            "    $e.printStackTrace();" +
            "    throw $e;" +  // 重新抛出异常
            "}",
            exceptionType
        );
    }

    /**
     * 测试服务类
     */
    public static class PaymentService {
        public void processPayment(String orderId, double amount) {
            if (amount <= 0) {
                throw new IllegalArgumentException("金额必须大于0");
            }
            if (orderId == null || orderId.isEmpty()) {
                throw new NullPointerException("订单号不能为空");
            }
            System.out.println("  [业务] 处理支付成功: " + orderId + " - " + amount);
        }

        public double calculateDiscount(double price, double rate) {
            if (rate > 1.0) {
                throw new IllegalArgumentException("折扣率不能大于1.0");
            }
            return price * rate;
        }
    }

    public static void main(String[] args) throws Exception {
        // 增强PaymentService
        Class<?> enhancedClass = addExceptionHandling(PaymentService.class);
        Object service = enhancedClass.newInstance();

        System.out.println("====== 测试异常捕获 ======\n");

        // 测试1: 正常情况
        System.out.println("1. 正常支付:");
        try {
            enhancedClass.getMethod("processPayment", String.class, double.class)
                .invoke(service, "ORDER-001", 99.99);
        } catch (Exception e) {
            // 忽略
        }

        // 测试2: 金额异常
        System.out.println("\n2. 金额异常:");
        try {
            enhancedClass.getMethod("processPayment", String.class, double.class)
                .invoke(service, "ORDER-002", -10.0);
        } catch (Exception e) {
            System.out.println("外部捕获到异常");
        }

        // 测试3: 空指针异常
        System.out.println("\n3. 空指针异常:");
        try {
            enhancedClass.getMethod("processPayment", String.class, double.class)
                .invoke(service, null, 100.0);
        } catch (Exception e) {
            System.out.println("外部捕获到异常");
        }

        // 测试4: 参数异常
        System.out.println("\n4. 参数异常:");
        try {
            enhancedClass.getMethod("calculateDiscount", double.class, double.class)
                .invoke(service, 100.0, 1.5);
        } catch (Exception e) {
            System.out.println("外部捕获到异常");
        }
    }
}

五、模拟Hibernate懒加载

5.1 实体代理生成

java 复制代码
package com.example.javassist.hibernate;

import javassist.*;

import java.lang.reflect.Method;

/**
 * 模拟Hibernate懒加载机制
 */
public class LazyLoadingProxy {

    /**
     * 实体类
     */
    public static class User {
        private Long id;
        private String username;
        private String email;

        // Lazy加载的关联对象
        private UserProfile profile;

        public User(Long id, String username) {
            this.id = id;
            this.username = username;
        }

        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }

        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }

        public String getEmail() { return email; }
        public void setEmail(String email) { this.email = email; }

        public UserProfile getProfile() { return profile; }
        public void setProfile(UserProfile profile) { this.profile = profile; }

        @Override
        public String toString() {
            return "User{id=" + id + ", username='" + username + "'}";
        }
    }

    public static class UserProfile {
        private String bio;
        private String avatar;

        public UserProfile(String bio, String avatar) {
            this.bio = bio;
            this.avatar = avatar;
        }

        public String getBio() { return bio; }
        public String getAvatar() { return avatar; }

        @Override
        public String toString() {
            return "UserProfile{bio='" + bio + "', avatar='" + avatar + "'}";
        }
    }

    /**
     * 创建懒加载代理
     */
    public static User createLazyProxy(Long userId, String username) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 创建代理类
        CtClass proxyClass = pool.makeClass("com.example.UserProxy$" + System.currentTimeMillis());
        proxyClass.setSuperclass(pool.get("com.example.javassist.hibernate.LazyLoadingProxy$User"));

        // 添加懒加载标记字段
        CtField loadedField = new CtField(CtClass.booleanType, "__profileLoaded", proxyClass);
        loadedField.setModifiers(Modifier.PRIVATE);
        proxyClass.addField(loadedField, "false");

        // 重写getProfile方法,实现懒加载
        CtMethod getProfileMethod = CtNewMethod.make(
            "public com.example.javassist.hibernate.LazyLoadingProxy$UserProfile getProfile() {" +
            "    if (!__profileLoaded) {" +
            "        System.out.println(\"[懒加载] 加载UserProfile,用户ID: \" + getId());" +
            "        " +
            "        try {" +
            "            Thread.sleep(100);" +  // 模拟数据库查询
            "        } catch (Exception e) {}" +
            "        " +
            "        com.example.javassist.hibernate.LazyLoadingProxy$UserProfile profile = " +
            "            new com.example.javassist.hibernate.LazyLoadingProxy$UserProfile(" +
            "                \"Bio of \" + getUsername(), " +
            "                \"avatar-\" + getId() + \".jpg\"" +
            "            );" +
            "        setProfile(profile);" +
            "        __profileLoaded = true;" +
            "    }" +
            "    return super.getProfile();" +
            "}",
            proxyClass
        );
        proxyClass.addMethod(getProfileMethod);

        // 创建代理实例
        Class<?> clazz = proxyClass.toClass();
        User proxy = (User) clazz.getConstructor(Long.class, String.class)
            .newInstance(userId, username);

        return proxy;
    }

    public static void main(String[] args) throws Exception {
        System.out.println("====== Hibernate懒加载模拟 ======\n");

        // 创建懒加载代理对象
        User user = createLazyProxy(1L, "zhangsan");

        System.out.println("1. 创建用户对象: " + user);
        System.out.println("   此时Profile尚未加载\n");

        System.out.println("2. 访问基本属性:");
        System.out.println("   用户名: " + user.getUsername());
        System.out.println("   ID: " + user.getId());
        System.out.println("   Profile仍未加载\n");

        System.out.println("3. 第一次访问Profile(触发懒加载):");
        UserProfile profile = user.getProfile();
        System.out.println("   Profile: " + profile);

        System.out.println("\n4. 第二次访问Profile(使用缓存):");
        profile = user.getProfile();
        System.out.println("   Profile: " + profile);
    }
}

六、Spring AOP实现

6.1 自定义注解与切面

java 复制代码
package com.example.javassist.spring;

import javassist.*;

import java.lang.annotation.*;

/**
 * 基于Javassist的简易AOP实现
 */
public class SimpleAOP {

    /**
     * 日志注解
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Log {
        String value() default "";
    }

    /**
     * 缓存注解
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Cacheable {
        int ttl() default 60;
    }

    /**
     * AOP增强器
     */
    public static class AOPEnhancer {

        public static Class<?> enhance(Class<?> targetClass) throws Exception {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.get(targetClass.getName());

            for (CtMethod method : ctClass.getDeclaredMethods()) {
                // 处理@Log注解
                if (hasAnnotation(method, Log.class)) {
                    addLogging(method);
                }

                // 处理@Cacheable注解
                if (hasAnnotation(method, Cacheable.class)) {
                    addCaching(method);
                }
            }

            return ctClass.toClass();
        }

        private static boolean hasAnnotation(CtMethod method, Class<? extends Annotation> annotationClass) {
            try {
                return method.hasAnnotation(annotationClass);
            } catch (ClassNotFoundException e) {
                return false;
            }
        }

        private static void addLogging(CtMethod method) throws CannotCompileException {
            method.insertBefore(
                "System.out.println(\"[LOG] 执行方法: " + method.getName() + "\");" +
                "System.out.println(\"[LOG] 参数: \" + java.util.Arrays.toString($args));"
            );

            method.insertAfter(
                "System.out.println(\"[LOG] 方法执行完成: " + method.getName() + "\");"
            );
        }

        private static void addCaching(CtMethod method) throws CannotCompileException {
            String cacheKey = method.getDeclaringClass().getSimpleName() + "." + method.getName();

            method.insertBefore(
                "String __cacheKey = \"" + cacheKey + "\" + java.util.Arrays.toString($args);" +
                "Object __cached = com.example.javassist.spring.SimpleAOP.CacheManager.get(__cacheKey);" +
                "if (__cached != null) {" +
                "    System.out.println(\"[CACHE] 命中缓存: \" + __cacheKey);" +
                "    return ($r) __cached;" +
                "}" +
                "System.out.println(\"[CACHE] 未命中,执行方法: \" + __cacheKey);"
            );

            method.insertAfter(
                "com.example.javassist.spring.SimpleAOP.CacheManager.put(__cacheKey, ($w) $_);"
            );
        }
    }

    /**
     * 简单缓存管理器
     */
    public static class CacheManager {
        private static final java.util.Map<String, Object> cache =
            new java.util.concurrent.ConcurrentHashMap<>();

        public static Object get(String key) {
            return cache.get(key);
        }

        public static void put(String key, Object value) {
            cache.put(key, value);
        }

        public static void clear() {
            cache.clear();
        }
    }

    /**
     * 业务服务类
     */
    public static class ProductService {

        @Log
        public String getProductName(Long id) {
            return "Product-" + id;
        }

        @Cacheable(ttl = 300)
        public Double getProductPrice(Long id) {
            simulateDbQuery();
            return 99.99;
        }

        @Log
        @Cacheable
        public String getProductDetail(Long id) {
            simulateDbQuery();
            return "Detail-" + id;
        }

        public void updateProduct(Long id, String name) {
            System.out.println("  [业务] 更新产品: " + id + " -> " + name);
        }

        private void simulateDbQuery() {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 增强ProductService
        Class<?> enhancedClass = AOPEnhancer.enhance(ProductService.class);
        Object service = enhancedClass.newInstance();

        System.out.println("====== 测试@Log注解 ======");
        enhancedClass.getMethod("getProductName", Long.class)
            .invoke(service, 100L);

        System.out.println("\n====== 测试@Cacheable注解 ======");
        enhancedClass.getMethod("getProductPrice", Long.class)
            .invoke(service, 200L);
        enhancedClass.getMethod("getProductPrice", Long.class)
            .invoke(service, 200L);  // 第二次调用,命中缓存

        System.out.println("\n====== 测试多注解 ======");
        enhancedClass.getMethod("getProductDetail", Long.class)
            .invoke(service, 300L);
        enhancedClass.getMethod("getProductDetail", Long.class)
            .invoke(service, 300L);  // 命中缓存

        System.out.println("\n====== 测试无注解方法 ======");
        enhancedClass.getMethod("updateProduct", Long.class, String.class)
            .invoke(service, 400L, "New Name");
    }
}

七、高级技巧与最佳实践

7.1 类文件的保存与加载

java 复制代码
package com.example.javassist.advanced;

import javassist.*;

import java.io.File;

/**
 * 保存和加载字节码文件
 */
public class ClassSaveLoadDemo {

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 创建类
        CtClass ctClass = pool.makeClass("com.example.SavedClass");

        // 添加方法
        CtMethod method = CtNewMethod.make(
            "public String sayHello() { return \"Hello from saved class!\"; }",
            ctClass
        );
        ctClass.addMethod(method);

        // 1. 保存为.class文件
        String outputPath = "./target/classes";
        ctClass.writeFile(outputPath);
        System.out.println("类文件已保存到: " + outputPath);

        // 2. 转换为字节数组
        byte[] bytecode = ctClass.toBytecode();
        System.out.println("字节码大小: " + bytecode.length + " bytes");

        // 3. 从已有的class文件加载
        CtClass loadedClass = pool.get("com.example.SavedClass");
        System.out.println("加载的类: " + loadedClass.getName());

        // 4. 使用自定义ClassLoader加载
        Class<?> clazz = ctClass.toClass();
        Object instance = clazz.newInstance();
        String result = (String) clazz.getMethod("sayHello").invoke(instance);
        System.out.println("调用结果: " + result);

        // 5. detach - 释放类(从ClassPool中移除)
        ctClass.detach();
        System.out.println("类已从ClassPool中移除");
    }
}

7.2 性能优化建议

java 复制代码
package com.example.javassist.advanced;

import javassist.*;

/**
 * Javassist性能优化技巧
 */
public class PerformanceOptimization {

    public static void main(String[] args) throws Exception {

        // 技巧1: 重用ClassPool
        ClassPool pool = ClassPool.getDefault();

        // 技巧2: 冻结类(freeze)- 避免后续修改
        CtClass ctClass1 = pool.makeClass("com.example.Class1");
        ctClass1.freeze();  // 冻结后性能更好,但不能再修改
        // ctClass1.addMethod(...);  // 会抛出异常

        // 技巧3: 使用 defrost 解冻
        ctClass1.defrost();  // 解冻后可以继续修改
        CtMethod method = CtNewMethod.make(
            "public void test() {}",
            ctClass1
        );
        ctClass1.addMethod(method);

        // 技巧4: 批量操作时使用 writeFile 而不是 toClass
        CtClass[] classes = new CtClass[100];
        for (int i = 0; i < 100; i++) {
            classes[i] = pool.makeClass("com.example.Batch" + i);
            // 添加方法...
        }

        // 批量写入文件(比逐个toClass快)
        for (CtClass cc : classes) {
            cc.writeFile("./target/classes");
        }

        // 技巧5: 及时释放不再使用的类
        for (CtClass cc : classes) {
            cc.detach();  // 从ClassPool中移除
        }

        // 技巧6: 使用编译好的代码而不是字符串拼接
        CtClass ctClass2 = pool.makeClass("com.example.OptimizedClass");

        // ✗ 不推荐:字符串拼接
        String code1 = "public void method1() { " +
            "int sum = 0; " +
            "for (int i = 0; i < 100; i++) { " +
            "sum += i; } }";

        // ✓ 推荐:使用StringBuilder或格式化
        StringBuilder code2 = new StringBuilder();
        code2.append("public void method2() {");
        code2.append("    int sum = 0;");
        code2.append("    for (int i = 0; i < 100; i++) {");
        code2.append("        sum += i;");
        code2.append("    }");
        code2.append("}");

        System.out.println("性能优化技巧演示完成");
    }
}

7.3 常见陷阱与解决方案

java 复制代码
package com.example.javassist.advanced;

import javassist.*;

/**
 * Javassist常见问题与解决方案
 */
public class CommonPitfalls {

    public static void main(String[] args) {

        try {
            ClassPool pool = ClassPool.getDefault();

            // 陷阱1: 重复加载类
            CtClass cc1 = pool.get("java.lang.String");
            CtClass cc2 = pool.get("java.lang.String");
            System.out.println("cc1 == cc2: " + (cc1 == cc2));  // true,同一个实例

            // 陷阱2: 修改系统类(会失败)
            try {
                CtClass stringClass = pool.get("java.lang.String");
                stringClass.addMethod(CtNewMethod.make(
                    "public void myMethod() {}",
                    stringClass
                ));
                stringClass.toClass();  // 会抛出异常
            } catch (Exception e) {
                System.out.println("✗ 无法修改系统类: " + e.getMessage());
            }

            // 陷阱3: 类已经加载后无法修改
            CtClass cc = pool.makeClass("com.example.LoadedClass");
            Class<?> clazz = cc.toClass();  // 类已加载

            try {
                cc.addMethod(CtNewMethod.make(
                    "public void newMethod() {}",
                    cc
                ));
                cc.toClass();  // 会失败
            } catch (Exception e) {
                System.out.println("✗ 类已加载,无法修改: " + e.getMessage());
            }

            // 解决方案:使用新的类名或detach后重新创建
            cc.detach();
            CtClass cc3 = pool.makeClass("com.example.LoadedClass2");
            cc3.addMethod(CtNewMethod.make(
                "public void newMethod() {}",
                cc3
            ));
            Class<?> clazz2 = cc3.toClass();
            System.out.println("✓ 使用新类名成功");

            // 陷阱4: 基本类型包装
            CtClass intClass = CtClass.intType;
            CtClass integerClass = pool.get("java.lang.Integer");
            System.out.println("int != Integer: " + (intClass != integerClass));

            // 陷阱5: ClassPool内存泄漏
            // 长时间运行的应用需要定期清理
            // pool.clearImportedPackages();  // 清理导入的包

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

八、实际应用:MyBatis插件开发

8.1 SQL执行监控插件

java 复制代码
package com.example.javassist.mybatis;

import javassist.*;

import java.lang.reflect.Method;

/**
 * 模拟MyBatis插件 - SQL执行监控
 */
public class MyBatisPlugin {

    /**
     * SQL执行统计
     */
    public static class SqlStats {
        private static long totalExecutions = 0;
        private static long totalTime = 0;

        public static void recordExecution(String sql, long duration) {
            totalExecutions++;
            totalTime += duration;
            System.out.printf("[SQL监控] 执行SQL: %s (耗时: %dms)%n", sql, duration);

            if (duration > 100) {
                System.err.printf("[慢SQL警告] SQL: %s (耗时: %dms)%n", sql, duration);
            }
        }

        public static void printStats() {
            long avgTime = totalExecutions > 0 ? totalTime / totalExecutions : 0;
            System.out.println("\n====== SQL执行统计 ======");
            System.out.println("总执行次数: " + totalExecutions);
            System.out.println("总耗时: " + totalTime + "ms");
            System.out.println("平均耗时: " + avgTime + "ms");
        }
    }

    /**
     * 增强Mapper接口
     */
    public static Class<?> enhanceMapper(Class<?> mapperInterface) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 创建实现类
        CtClass implClass = pool.makeClass(
            mapperInterface.getName() + "Impl$" + System.currentTimeMillis()
        );
        implClass.addInterface(pool.get(mapperInterface.getName()));

        // 实现所有接口方法
        for (Method method : mapperInterface.getMethods()) {
            addMethodImpl(implClass, method);
        }

        return implClass.toClass();
    }

    private static void addMethodImpl(CtClass implClass, Method method) throws Exception {
        StringBuilder methodBody = new StringBuilder();
        methodBody.append("public ").append(method.getReturnType().getName())
            .append(" ").append(method.getName()).append("(");

        // 参数
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            if (i > 0) methodBody.append(", ");
            methodBody.append(paramTypes[i].getName()).append(" arg").append(i);
        }
        methodBody.append(") {");

        // 方法体
        String sql = "SELECT * FROM table WHERE id = ?";  // 模拟SQL
        methodBody.append("    String __sql = \"").append(sql).append("\";");
        methodBody.append("    long __start = System.currentTimeMillis();");
        methodBody.append("    try { Thread.sleep(").append((int)(Math.random() * 150)).append("); } catch(Exception e) {}");
        methodBody.append("    long __duration = System.currentTimeMillis() - __start;");
        methodBody.append("    com.example.javassist.mybatis.MyBatisPlugin.SqlStats.recordExecution(__sql, __duration);");

        // 返回值
        if (!method.getReturnType().equals(void.class)) {
            if (method.getReturnType().equals(String.class)) {
                methodBody.append("    return \"Result\";");
            } else if (method.getReturnType().equals(int.class)) {
                methodBody.append("    return 1;");
            } else {
                methodBody.append("    return null;");
            }
        }

        methodBody.append("}");

        CtMethod ctMethod = CtNewMethod.make(methodBody.toString(), implClass);
        implClass.addMethod(ctMethod);
    }

    /**
     * Mapper接口
     */
    public interface UserMapper {
        String findById(Long id);
        int insert(String name, int age);
        int update(Long id, String name);
        int delete(Long id);
    }

    public static void main(String[] args) throws Exception {
        // 增强Mapper
        Class<?> mapperImpl = enhanceMapper(UserMapper.class);
        UserMapper mapper = (UserMapper) mapperImpl.newInstance();

        System.out.println("====== 测试MyBatis插件 ======\n");

        // 执行SQL
        mapper.findById(1L);
        mapper.insert("张三", 25);
        mapper.update(1L, "李四");
        mapper.findById(2L);
        mapper.delete(1L);

        // 打印统计
        SqlStats.printStats();
    }
}

九、总结与最佳实践

9.1 使用建议总结

bash 复制代码
┌─────────────────────────────────────────────────────┐
│              Javassist最佳实践                       │
├─────────────────────────────────────────────────────┤
│  ✓ 优先使用源代码级API(易读易维护)                │
│  ✓ 合理使用ClassPool(避免内存泄漏)                │
│  ✓ 善用特殊变量($0, $1, $_, 等)                  │
│  ✓ 注意类加载时机(避免重复加载)                   │
│  ✓ 异常处理要完善(字节码操作容易出错)             │
│  ✓ 性能敏感场景考虑缓存生成的类                     │
│  ✗ 避免修改JDK核心类                                │
│  ✗ 避免过度使用(简单场景用反射即可)               │
│  ✗ 不要在生产环境频繁动态生成类                     │
└─────────────────────────────────────────────────────┘

9.2 应用场景选择

场景 推荐方案 理由
AOP日志 Javassist 简单直观,易维护
ORM代理 Javassist Hibernate的选择
性能监控 Javassist/ByteBuddy 都可以,看团队熟悉度
Agent开发 ByteBuddy API更现代
底层优化 ASM 性能最优

9.3 学习路线

markdown 复制代码
学习路线图:
1. 基础知识
   └─ Java反射机制
   └─ JVM类加载机制
   └─ 字节码基础概念

2. Javassist入门
   └─ ClassPool与CtClass
   └─ 创建类与方法
   └─ 特殊变量使用

3. 进阶技巧
   └─ 方法拦截
   └─ 异常处理
   └─ 性能优化

4. 实战项目
   └─ AOP框架
   └─ ORM代理
   └─ 监控系统

9.4 参考资源

Javassist作为Java字节码操作的经典框架,以其简洁的API和强大的功能,成为了众多开源框架的首选。掌握Javassist,能够让你在AOP、动态代理、性能监控等领域游刃有余!


相关推荐
极客Bob20 分钟前
Java 集合操作完整清单(Java 8+ Stream API)
java
Knight_AL29 分钟前
JWT 无状态认证深度解析:原理、优势
java·jwt
寒山李白1 小时前
IDEA中如何配置Java类注释(Java类注释信息配置,如作者、备注、时间等)
java
我要添砖java1 小时前
<JAVAEE> 多线程4-wait和notify方法
android·java·java-ee
Rysxt_1 小时前
Spring Boot SPI 教程
java·数据库·sql
海边夕阳20061 小时前
主流定时任务框架对比:Spring Task/Quartz/XXL-Job怎么选?
java·后端·spring·xxl-job·定时任务·job
q***98521 小时前
VS Code 中如何运行Java SpringBoot的项目
java·开发语言·spring boot
帧栈1 小时前
开发避坑指南(72):HttpHeaders 的add()方法和set()方法有什么区别?
java·spring·http
unclecss1 小时前
把 Spring Boot 的启动时间从 3 秒打到 30 毫秒,内存砍掉 80%,让 Java 在 Serverless 时代横着走
java·jvm·spring boot·serverless·graalvm