目录
一、理解 class 与 interface 的本质差异
1.1 设计定位的本质区别
1.2 实例化能力的根本差异
1.3 接口引用的本质
二、动态代理:无需实现类的接口实例化
2.1 从静态到动态的思维转变
2.2 动态代理的核心工作原理
2.3 动态代理的三大核心要素
三、代理模式:动态代理的设计根基
3.1 代理模式的核心思想
3.2 静态代理的实现与局限
3.3 动态代理对静态代理的革命性改进
四、JDK 动态代理:基于接口的代理实现
4.1 核心类库的深度解析
4.2 完整实战:日志增强场景
4.3 代理类生成的幕后细节
五、CGLIB 动态代理:无接口场景的解决方案
5.1 CGLIB 的核心优势
5.2 实战案例:代理无接口的用户数据访问类
5.3 CGLIB 的字节码增强原理
六、两种动态代理的深度对比与选择策略
6.1 核心差异对比表
6.2 场景选择最佳实践
优先选择 JDK 动态代理的场景:
优先选择 CGLIB 的场景:
七、动态代理在实际框架中的应用
7.1 Spring AOP 的代理选择策略
7.2 Hibernate 的懒加载代理
7.3 RPC 框架中的远程代理
八、新手常见问题与解答
8.1 为什么 JDK 代理的 invoke 方法参数有 proxy?
8.2 CGLIB 代理中 invoke 和 invokeSuper 的区别
8.3 动态代理对性能的影响有多大?
九、小结
在 Java 面向对象体系中,class 与 interface 是构建程序的两大基石,它们的核心区别可以从以下维度理解:
StringBuilder
类实现了字符串操作的具体逻辑CharSequence
接口定义了字符序列的读取行为// 合法操作:实例化非抽象类
StringBuilder sb = new StringBuilder();
// 非法操作:无法直接实例化接口
// CharSequence cs = new CharSequence(); 编译错误
// 正确用法:通过实现类向上转型
CharSequence cs = new StringBuilder();
当我们写下CharSequence cs = new StringBuilder()
时,JVM 实际完成了两件事:
StringBuilder
实例(实现类对象)CharSequence
接口变量(向上转型)这种设计体现了 Java 的 "面向接口编程" 思想:程序依赖抽象而非具体实现,为动态代理的实现奠定了基础。
假设我们需要一个Hello
接口的实例,但不想编写具体实现类:
静态思维:必须编写实现类
interface Hello { void say(String name); }
class HelloImpl implements Hello {
public void say(String name) {
System.out.println("Hello, " + name);
}
}
Hello hello = new HelloImpl();
动态思维:利用 JDK 动态代理
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[]{Hello.class},
(proxy, method, args) -> {
if ("say".equals(method.getName())) {
System.out.println("动态问候:" + args[0]);
}
return null;
}
);
hello.say("Java"); // 直接调用接口方法
动态代理的本质是 JVM 在运行时的 "类工厂" 机制:
我们可以通过反编译看到代理类的核心结构:
public final class $Proxy0 extends Proxy implements Hello {
private static Method sayMethod;
public $Proxy0(InvocationHandler h) { super(h); }
public void say(String name) {
// 所有方法调用转发给InvocationHandler
super.h.invoke(this, sayMethod, new Object[]{name});
}
static {
// 通过反射预获取方法对象
sayMethod = Hello.class.getMethod("say", String.class);
}
}
代理模式的本质是 "控制对象访问",其核心角色包括:
UserService
接口UserServiceImpl
以用户服务为例,静态代理的典型实现:
interface UserService { void query(int id); }
class UserServiceImpl implements UserService {
public void query(int id) {
System.out.println("查询用户ID:" + id);
}
}
class UserProxy implements UserService {
private UserService target;
public UserProxy(UserService target) { this.target = target; }
public void query(int id) {
System.out.println("【前置日志】开始查询");
target.query(id);
System.out.println("【后置日志】查询完成");
}
}
静态代理的致命缺陷:
动态代理通过 "运行时生成代理类" 解决了静态代理的痛点:
JDK 动态代理的核心在java.lang.reflect
包中:
newProxyInstance
Object invoke(Object proxy, Method method, Object[] args)
proxy
:代理对象本身method
:被调用的方法对象args
:方法参数数组// 定义服务接口
interface UserService {
void query(int id);
String update(String name);
}
// 真实服务实现
class UserServiceImpl implements UserService {
public void query(int id) {
System.out.println("查询用户ID:" + id);
}
public String update(String name) {
System.out.println("更新用户名为:" + name);
return "操作成功";
}
}
// 日志处理器:实现InvocationHandler
class LogHandler implements InvocationHandler {
private Object target; // 被代理的目标对象
public LogHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:记录开始时间
System.out.println("[" + new Date() + "] 开始调用方法:" + method.getName());
// 调用目标方法(核心业务逻辑)
Object result = method.invoke(target, args);
// 后置增强:记录结束时间
System.out.println("[" + new Date() + "] 结束调用方法:" + method.getName());
return result; // 返回方法执行结果
}
}
// 客户端调用
public class JDKProxyDemo {
public static void main(String[] args) {
// 创建真实服务实例
UserService realService = new UserServiceImpl();
// 创建InvocationHandler
InvocationHandler handler = new LogHandler(realService);
// 生成代理实例(关键三步)
UserService proxy = (UserService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(), // 类加载器
realService.getClass().getInterfaces(), // 接口列表
handler // 调用处理器
);
// 调用代理方法,实际执行增强逻辑
proxy.query(1001);
String result = proxy.update("张三");
System.out.println("返回结果:" + result);
}
}
通过设置系统属性sun.misc.ProxyGenerator.saveGeneratedFiles=true
,可以保存生成的代理类:
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
反编译生成的$Proxy0.class
可以看到:
Proxy
并实现目标接口super.h.invoke()
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1, m2, m3; // 方法对象缓存
public $Proxy0(InvocationHandler h) { super(h); }
public void query(int id) {
try {
// 核心转发逻辑
super.h.invoke(this, m2, new Object[]{id});
} catch (Throwable t) { /* 异常处理 */ }
}
static {
try {
// 通过反射预获取方法,提升性能
m1 = Object.class.getMethod("equals", Object.class);
m2 = UserService.class.getMethod("query", int.class);
m3 = UserService.class.getMethod("update", String.class);
} catch (Exception e) { /* 异常处理 */ }
}
}
当需要代理没有接口的普通类时,JDK 动态代理会失效,此时 CGLIB 成为最佳选择:
// 无接口的目标类
class UserDao {
public void query(int id) {
System.out.println("查询用户ID:" + id);
}
public String update(String name) {
System.out.println("更新用户名为:" + name);
return "操作成功";
}
}
// CGLIB方法拦截器
class LogInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 前置增强
System.out.println("[" + new Date() + "] 开始调用:" + method.getName());
// 调用父类方法(关键:避免递归调用)
Object result = proxy.invokeSuper(obj, args);
// 后置增强
System.out.println("[" + new Date() + "] 结束调用:" + method.getName());
return result;
}
}
// 客户端调用
public class CGLIBProxyDemo {
public static void main(String[] args) {
// 创建CGLIB增强器
Enhancer enhancer = new Enhancer();
// 设置父类(被代理类)
enhancer.setSuperclass(UserDao.class);
// 设置方法拦截器
enhancer.setCallback(new LogInterceptor());
// 生成代理实例(核心步骤)
UserDao proxy = (UserDao) enhancer.create();
// 调用代理方法
proxy.query(1001);
String result = proxy.update("李四");
System.out.println("返回结果:" + result);
}
}
CGLIB 的代理过程分为四步:
反编译生成的代理类可以看到:
public class UserDao$$EnhancerByCGLIB$$a extends UserDao {
private LogInterceptor cglib$interceptor;
public void query(int id) {
if (cglib$interceptor != null) {
// 调用拦截器处理方法调用
cglib$interceptor.intercept(this,
UserDao.class.getMethod("query", int.class),
new Object[]{id},
cglib$methodProxy1);
} else {
super.query(id); // 原始方法调用
}
}
// 代理类初始化逻辑...
}
对比维度 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理基础 | 接口实现 | 类继承 |
核心类 | Proxy + InvocationHandler | Enhancer + MethodInterceptor |
字节码技术 | 反射生成 | ASM 直接操作 |
代理限制 | 不能代理类,不能代理 final 方法 | 不能代理 final 类和方法 |
性能表现 | 首次调用开销大,后续稳定 | 首次生成开销更大,运行时效率更高 |
依赖要求 | JDK 内置支持 | 需要 cglib-nodep.jar |
Spring 框架中的 AOP 实现充分利用了动态代理:
proxy-target-class=true
配置
Hibernate 使用 CGLIB 代理实现实体的懒加载:
// 加载用户时返回代理对象
User user = session.get(User.class, 1L); // 返回CGLIB代理对象
// 实际访问属性时才触发数据库查询
System.out.println(user.getUsername()); // 此时才执行SQL查询
Dubbo 框架使用动态代理隐藏远程调用细节:
// 声明服务接口(无实现类)
public interface UserService {
User getById(int id);
}
// 通过代理直接调用远程方法
UserService userService = dubboContext.getBean(UserService.class);
User user = userService.getById(1001); // 实际触发网络调用
invoke
方法的proxy
参数代表代理对象本身,在以下场景有用:
public Object invoke(Object proxy, Method method, Object[] args) {
if (method.getName().equals("toString")) {
return "动态代理对象:" + proxy.hashCode();
}
// 其他逻辑...
}
method.invoke(obj, args)
:递归调用代理方法(导致死循环)proxy.invokeSuper(obj, args)
:调用父类原始方法(正确方式)动态代理作为 Java 反射机制的高级应用,其核心价值在于:
掌握动态代理技术,不仅能深入理解 Spring、Hibernate 等框架的底层实现,更能在实际开发中应用代理模式解决复杂问题,是 Java 工程师从初级迈向中级的重要里程碑。
参考链接:
小旋锋 的个人主页 - 文章 - 掘金
Java三种代理模式:静态代理、动态代理和cglib代理 - 个人文章 - SegmentFault 思否
Java 动态代理详解动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架 - 掘金
深入理解Java动态代理 - 知乎