Skip to content

Commit 68459b3

Browse files
committed
update:Java 代理面试题补充
1 parent 7854144 commit 68459b3

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

docs/java/basis/java-basic-questions-03.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,56 @@ public class DebugInvocationHandler implements InvocationHandler {
397397

398398
像 MyBatis、Hibernate 这种框架,能帮你把数据库查出来的一行行数据,自动变成一个个 Java 对象。它是怎么知道数据库字段对应哪个 Java 属性的?还是靠反射。它通过反射获取 Java 类的属性列表,然后把查询结果按名字或配置对应起来,再用反射调用 setter 或直接修改字段值。反过来,保存对象到数据库时,也是用反射读取属性值来拼 SQL。
399399

400+
## 代理
401+
402+
关于 Java 代理的详细介绍,可以看看笔者写的 [Java 代理模式详解](https://javaguide.cn/java/basis/proxy.html "Java 代理模式详解")这篇文章。
403+
404+
### 如何实现动态代理?
405+
406+
动态代理是一种非常强大的设计模式,它允许我们在**不修改源代码**的情况下,对一个类或对象的方法进行**功能增强(Enhancement)**
407+
408+
在 Java 中,实现动态代理最主流的方式有两种:**JDK 动态代理****CGLIB 动态代理**
409+
410+
**第一种:JDK 动态代理**
411+
412+
Java 官方提供的,其核心要求是目标类必须实现一个或多个接口。JDK 动态代理在运行时,会利用 `Proxy.newProxyInstance()` 方法,动态地创建一个实现了这些接口的代理类的实例。这个代理类在内存中生成,你看不到它的 `.java``.class` 文件。
413+
414+
当你调用代理对象的任何一个方法时,这个调用都会被转发到我们提供的一个 `InvocationHandler` 接口的 `invoke` 方法中。在 `invoke` 方法里,我们就可以在调用原始方法(目标方法)之前或之后,加入我们自己的增强逻辑。
415+
416+
**第二种:CGLIB 动态代理**
417+
418+
CGLIB 是一个第三方的代码生成库。它的原理与 JDK 完全不同,它不要求被代理的类实现接口。它在运行时,动态生成目标类的子类作为代理类(通过 ASM 字节码操作技术)。然后,它会重写父类(也就是被代理类)中所有非 `final``private``static` 的方法。
419+
420+
当你调用代理对象的任何一个方法时,这个调用会被 CGLIB 的 `MethodInterceptor` 接口的 `intercept` 方法拦截。和 `InvocationHandler``invoke` 方法一样,我们可以在 `intercept` 方法里,在调用原始的父类方法之前或之后,加入我们的增强逻辑。
421+
422+
### 静态代理和动态代理有什么区别?
423+
424+
静态代理和动态代理的核心差异在于 **代理关系的确定时机、实现灵活性及维护成本**
425+
426+
| 对比维度 | 静态代理 (Static Proxy) | 动态代理 (Dynamic Proxy) |
427+
| ---------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
428+
| 代理关系确定时机 | 编译期(编译后生成固定的 `.class` 字节码文件) | 运行时(动态生成代理类字节码并加载到 JVM) |
429+
| 实现方式 | 手动编写代理类,需与目标类实现同一接口,一对一绑定 | 无需手动编写代理类,通过 `Handler`/`Interceptor` 封装增强逻辑,一对多复用 |
430+
| 接口依赖 | 必须实现接口(代理类与目标类遵循同一接口规范) | 支持代理接口或直接代理实现类 |
431+
| 代码量与维护性 | 代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改 | 代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑 |
432+
| 核心优势 | 实现简单、逻辑直观,无额外框架依赖 | 灵活性强、复用性高,降低重复编码,适配复杂场景 |
433+
| 典型应用场景 | 简单的装饰器模式、少量固定类的增强需求 | Spring AOP、RPC 框架(如 Dubbo)、ORM 框架 |
434+
435+
### ⭐️JDK 动态代理和 CGLIB 动态代理有什么区别?
436+
437+
1. JDK 动态代理是官方的,它要求被代理的类必须实现接口。它的原理是动态生成一个接口的实现类来作为代理。CGLIB 是第三方的,它不需要接口。它的原理是动态生成一个被代理类的子类来作为代理。但也正因为是继承,所以它不能代理 `final` 的类,被代理的方法也不能是 `final``private`
438+
2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
439+
440+
### ⭐️介绍一下动态代理在框架中的实际应用场景
441+
442+
动态代理最典型的应用场景就是**Spring AOP**
443+
444+
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
445+
446+
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示:
447+
448+
![SpringAOPProcess](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/230ae587a322d6e4d09510161987d346.jpeg)
449+
400450
## 注解
401451

402452
### 何谓注解?

docs/java/basis/proxy.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,21 @@ after method send
394394

395395
### 3.3. JDK 动态代理和 CGLIB 动态代理对比
396396

397-
1. **JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。** 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法,private 方法也无法代理
397+
1. JDK 动态代理是官方的,它要求被代理的类必须实现接口。它的原理是动态生成一个接口的实现类来作为代理。CGLIB 是第三方的,它不需要接口。它的原理是动态生成一个被代理类的子类来作为代理。但也正因为是继承,所以它不能代理 `final` 的类,被代理的方法也不能是 `final``private`
398398
2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
399399

400400
## 4. 静态代理和动态代理的对比
401401

402-
1. **灵活性**:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
403-
2. **JVM 层面**:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
402+
静态代理和动态代理的核心差异在于 **代理关系的确定时机、实现灵活性及维护成本**
403+
404+
| 对比维度 | 静态代理 (Static Proxy) | 动态代理 (Dynamic Proxy) |
405+
| ---------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
406+
| 代理关系确定时机 | 编译期(编译后生成固定的 `.class` 字节码文件) | 运行时(动态生成代理类字节码并加载到 JVM) |
407+
| 实现方式 | 手动编写代理类,需与目标类实现同一接口,一对一绑定 | 无需手动编写代理类,通过 `Handler`/`Interceptor` 封装增强逻辑,一对多复用 |
408+
| 接口依赖 | 必须实现接口(代理类与目标类遵循同一接口规范) | 支持代理接口或直接代理实现类 |
409+
| 代码量与维护性 | 代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改 | 代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑 |
410+
| 核心优势 | 实现简单、逻辑直观,无额外框架依赖 | 灵活性强、复用性高,降低重复编码,适配复杂场景 |
411+
| 典型应用场景 | 简单的装饰器模式、少量固定类的增强需求 | Spring AOP、RPC 框架(如 Dubbo)、ORM 框架 |
404412

405413
## 5. 总结
406414

0 commit comments

Comments
 (0)