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 参考资源
- 官方文档 : www.javassist.org/
- GitHub : github.com/jboss-javas...
- 教程 : www.baeldung.com/javassist
- 实战项目: Hibernate、MyBatis等ORM框架源码
Javassist作为Java字节码操作的经典框架,以其简洁的API和强大的功能,成为了众多开源框架的首选。掌握Javassist,能够让你在AOP、动态代理、性能监控等领域游刃有余!