一、jdk动态代理
1.1 概念和流程
jdk动态代理,是基于接口的代理。其工作原理可概括为在运行时动态生成实现指定接口的代理类,代理类会拦截所有接口方法的调用,将调用转发给一个 InvocationHandler ,InvocationHandler在invoke方法中进行逻辑的增强,或通过反射调用目标对象的真实方法。
工作流程如下:
客户端调用代理对象方法 → 代理类转发到 InvocationHandler → InvocationHandler执行 invoke 方法
1.1.2 Proxy.newProxyInstance() 生成代理类
动态生成代理类的过程如下,Proxy.newProxyInstance()是Java反射API中的一个静态方法,它允许在运行时创建一个实现类指定接口的代理类实例:

函数包含三个参数:
① loader, 定义代理类的类加载器;通常使用目标类对象的类加载器。
② interfaces,定义代理类所需实现的接口。
③ h,一个实现了 InvocationHandler 接口的对象,负责处理代理类方法的调用。
当调用Proxy.newProxyInstance() 时,JDK会
① 生成字节码:根据传入的接口 interfaces,动态生成一个实现接口的代理类($Proxy0, $Proxy1等);
② 编译加载:将生成的字节码编译为class对象,并通过指定的类加载器加载到JVM中。
③ 实例化代理类:创建代理类的实例,并将其与 h 绑定。
1.1.3 h, 实现InvocationHandler 接口
InvocationHandler是一个函数式接口,只有一个invoke函数,如下:

包含三个参数:
① proxy:代理对象本身(在函数体中慎用,避免递归调用);
② method:被调用的方法对象;
③ args:方法调用时的参数
当代理类会拦截到接口方法的调用时,会将该调用转发给 InvocationHandler,InvocationHandler就会自动执行 invoke 方法然后返回结果。
1.2 代码实例
定义一个StudentI 接口,包含 study 和 eat 两个方法:
public interface StudentI {
public void study(String studentName);
public void eat();
}
定义一个实现了 StudentI 的类 StudentImpl:
public class StudentImpl implements StudentI{
@Override
public void study(String studentName) {
System.out.println(studentName + " is studying...");
}
@Override
public void eat() {
System.out.println("eating...");
}
}
接下来,为StudentI生成代理类 StudentProxy,在 invoke 中插入增强代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class StudentProxy implements InvocationHandler {
private Object target; // 代理目标,要代理的对象
// 生成代理对象的方法
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
// classLoader, interface, invocationHandler: 动态代理方法在执行时,会调用h里的invoke方法去执行
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* proxy 为代理对象,就是bind方法生成的对象
* method 为当前调度的方法
* args为调度方法的参数
*/
if (method.getName().equals("study")) { // 增强study方法
System.out.println("proxy the study method, jdk, study before");
Object object = method.invoke(target, args);
System.out.println("jdk,, study after");
return object;
}
if (method.getName().equals("eat")){ // 增强eat方法
System.out.println("proxy the eat method, jdk, eat before");
Object object = method.invoke(target, args);
System.out.println("jdk,, eat after");
return object;
}
return method.invoke(target, args);
}
}
Main函数中测试一下:
public class TestMain {
public static void main(String[] args) {
StudentProxy stuProxy = new StudentProxy();
StudentI stuImplProxy =(StudentI) stuProxy.bind(new StudentImpl());
stuImplProxy.study("kiki");
stuImplProxy.eat();
}
}
运行结果如下:

1.3 jdk动态代理的应用
① SpringAOP默认使用的是 jdk 动态代理。SpringAOP 通过 jdk 动态代理实现了运行时、基于接口的方法拦截,结合 AspectJ 注解(如 @Before、@Around),可优雅地实现事务管理、日志记录、权限校验等横切关注点。
② Mybatis 的 Mapper 接口的工作原理也是 jdk动态代理。Mybatis运行时会使用 jdk 动态代理为Mapper接口生成代理对象 proxy, 代理对象会拦截接口方法,转而执行 mapperStatement 所代表的sql, 然后将 sql 执行结果返回。
二、cglib动态代理
2.1 概念和流程
cglib 动态代理,是基于继承的代理。它通过字节码技术在运行时为类创建代理子类,实现方法拦截和增强。
cglib 工作流程如下:
① 继承目标类。通过ASM(Java字节码操作框架,生成字节码、编译加载类、实例化)动态生成目标类的子类 ($$EnhancerByCGLIB)。
② 重写方法。在子类中重写目标类的方法,插入拦截逻辑。
③ MethodInterceptor 拦截。代理对象的方法调用会被转发到 MethodInterceptor,由其决定是否执行目标方法或增强逻辑。
2.1.1 Enhancer 生成代理对象,配置拦截器
Enhancer 是 cglib 的核心类,用于创建代理对象:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class); // 设置父类(目标类)
enhancer.setCallback(new MyMethodInterceptor()); // 设置拦截器
TargetClass proxy = (TargetClass) enhancer.create(); // 创建代理对象
当调用 enhancer.create()时,cglib会:
① 分析目标类的所有方法,
② 为每个可代理的方法(非private、非final)生成对应的methodProxy实例
③ 将这些实例存储在代理类(对应上面的proxy)中,作为静态字段或方法局部变量。
2.1.2 拦截器 MethodInterceptor
MethodInterceptor 是一个函数时接口,仅包含 intercept 函数:

四个参数:
obj:代理对象本身。
method: 被拦截的方法。
args:方法参数。
proxy:代理对象,可以用于高效调用父类方法(proxy.invokeSuper),比反射更快。
2.2 代码实例
生成一个PersonService类,有一个 sayHello() 方法,是需要被代理的类。
public class PersonService {
public void sayHello(String name) {
System.out.println("hello: " + name);
}
}
在TestCase的main函数中实现代理:
public class TestCase {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class); // 设置父类,即代理目标,代理对象
enhancer.setCallback((MethodInterceptor) (obj,method,in_args,proxy)->{ // 设置拦截器MethodInterceptor,这里是lambda表达式实现函数式接口
System.out.println("before");
Object result = proxy.invokeSuper(obj,in_args); // 调用父类,即目标类的方法
// cglib 性能更高效的原因在此,通过MethodProxy.invokeSuper调用目标类的方法,内部通过字节码生成直接调用父类的方法,避免反射
// jdk 通过 method.invoke 反射通过方法名和参数类型动态查找并调用方法,设计类加载、方法解析等,性能开销更大!
System.out.println("after");
return result;
});
PersonService personServiceProxy = (PersonService) enhancer.create();
personServiceProxy.sayHello("xixi");
}
}
PersonService personServiceProxy = (PersonService) enhancer.create();
personServiceProxy.sayHello("xixi");
上述两句代码执行后,解析拦截器 MethodInterceptor 对应的实参:
obj:代理类的实例本身,也就是 personServiceProxy;
method:PersonService.sayHello 方法对象;
in_args: 函数参数,“xixi”;
proxy:cglib通过字节码技术已生成sayHello方法对应的methodProxy实例,直接通过 proxy.invokeSuper即可调用。
// cglib 性能更高效的原因在此,通过MethodProxy.invokeSuper调用目标类的方法,内部通过字节码生成直接调用父类的方法,避免反射
// jdk 通过 method.invoke 反射通过方法名和参数类型动态查找并调用方法,设计类加载、方法解析等,性能开销更大!
2.3 cglib动态代理的应用
① 当目标对象实现接口时,SpringAOP 默认用jdk动态代理;如果目标对象未实现接口,SpringAOP 使用 cglib 动态代理。
② SpringBoot2 AOP 默认使用 cglib 动态代理。
SpringBoot 2.x 为何默认使用 Cglib 参考SpringBoot 2.x 为何默认使用 Cglib:


1586

被折叠的 条评论
为什么被折叠?



