ByteBuddy动态代理
java17以后替换cglib方式的动态代理。他和cglib实现动态代理的优势是不用定义接口也不需要去实现接口,对现有的代码扩展更友好。
- pom文件添加依赖
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<!-- 我用的最新的, 一般1.14.0以上都没问题 -->
<version>1.15.1</version>
</dependency>
- 先创建要被代理的类
public class TargetClass {
public void performAction() {
System.out.println("原始方法: performAction");
}
public String sayHello(String name) {
return "Hello, " + name;
}
}
- 创建拦截器
import net.bytebuddy.implementation.bind.annotation.*;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* 通用方法拦截器
*/
public class GenericInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method,
@AllArguments Object[] args,
@This Object obj,
@SuperCall Callable<?> callable) throws Exception {
// 获取动态添加的字段值
String proxyField = (String) obj.getClass().getField("proxyField").get(obj);
System.out.println("[拦截] proxyField 值: " + proxyField);
// 修改字段值
System.out.println("[通用拦截] 修改字段proxyField值: Modified by interceptor" );
obj.getClass().getField("proxyField").set(obj, "Modified by interceptor");
System.out.println("[通用拦截] 方法开始: " + method.getName());
Object result = callable.call(); // 执行原始方法
System.out.println("[通用拦截] 方法结束: " + method.getName());
return result;
}
}
- 代理工具
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 通用 Byte Buddy 动态代理工具类
*/
public class ByteBuddyProxy {
/**
* 创建一个代理对象
*
* @param targetClass 目标类类型
* @param interceptor 拦截器类(需使用 Byte Buddy 注解定义逻辑)
* @param <T> 泛型
* @return 代理实例
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
public static <T> T createProxy(Class<T> targetClass, Class<?> interceptor) throws Exception {
Class<?> dynamicType = new ByteBuddy()
.subclass(targetClass)
// ElementMatchers.any() 拦截所有方法
.method(ElementMatchers.any())
// MethodDelegation.to(interceptor) 拦截器类 方法增强
.intercept(MethodDelegation.to(interceptor))
// 动态的添加字段。这里可以放到参数里面,然后由调用方传递进来要添加什么参数
.defineField("proxyField", String.class, Visibility.PUBLIC) // 可选:添加字段
.defineField("abc", String.class, Visibility.PUBLIC) // 可选:添加字段
.make()
.load(targetClass.getClassLoader())
.getLoaded();
// 支持构造实例
// .getDeclaredConstructor()
// .newInstance();
Constructor<?> constructor = dynamicType.getDeclaredConstructor();
Object instance = constructor.newInstance();
// 现在就可以 调用方法触发拦截逻辑 了
// ((TargetClass) instance).performAction();
// 设置字段值
System.out.println("[通用拦截] 设置字段proxyField值: Hello Dynamic Field" );
dynamicType.getField("proxyField").set(instance, "Hello Dynamic Field");
System.out.println("[通用拦截] 设置字段abc值: def" );
dynamicType.getField("abc").set(instance, "def");
return (T) instance;
}
}
- 一个测试类用来测试动态代理
public class ByteBuddyGenericExample {
public static void main(String[] args) throws Exception {
// 创建代理对象
TargetClass proxyInstance = ByteBuddyProxy
.createProxy(TargetClass.class, GenericInterceptor.class);
// 调用方法
proxyInstance.performAction();
String result = proxyInstance.sayHello("World");
System.out.println("返回值: " + result);
String abc = (String) proxyInstance.getClass().getField("abc").get(proxyInstance);
System.out.println("last在打印一遍proxyField: " + abc);
}
}