AI面试总结:int和Integer的区别
前言 在Java面试中,int和Integer之前的区别是经...
简单记录一下Java面试中的静态代理和动态代理相关知识,记录一下,方便自己查阅。
代理模式:为对象提供一个代理对象,由代理对象控制对目标对象的访问。
可以在不修改目标对象代码的情况下增强功能
典型场景:日志、事务、权限、监控、AOP
定义:代理类在编译期就已经存在,手动编写代理类
接口
public interface UserService { void save(); }
目标类
public class UserServiceImpl implements UserService { public void save() { System.out.println("保存用户"); } }
代理类
public class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy(UserService target) { this.target = target; } @Override public void save() { System.out.println("开始事务"); target.save(); System.out.println("提交事务"); } }
使用
UserService service = new UserServiceProxy(new UserServiceImpl()); service.save();
结构简单,容易理解
每个接口都要写一个代理类
接口变动 → 代理类也要改
扩展性差
定义:代理类在运行时动态生成,不需要手写代理类。
Java 中主要有两种:
| 动态代理方式 | 核心机制 |
|---|---|
| JDK 动态代理 | 反射 + 接口 |
| CGLIB 动态代理(这里不介绍) | 字节码增强(继承) |
这里我只介绍JDK动态代理
目标类必须实现接口
使用java.lang.reflect.Proxy
PS:JDK动态代理基于接口和反射实现,要求目标类实现接口
public class LogHandler implements InvocationHandler { private Object target; public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法前:" + method.getName()); Object result = method.invoke(target, args); System.out.println("方法后:" + method.getName()); return result; } } UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, new LogHandler(new UserServiceImpl()) ); proxy.save();
运行时生成一个实现了接口的代理类
方法调用 → 转发给 InvocationHandler.invoke()
代理类名类似:
com.sun.proxy.$Proxy0| 对比点 | 静态代理 | JDK 动态代理 |
|---|---|---|
| 代理类生成时机 | 编译期 | 运行期 |
| 是否手写代理类 | ✅ | ❌ |
| 是否依赖接口 | ✅ | ✅ |
| 实现方式 | 实现接口 | 反射 |
| 性能 | 高 | 稍低 |
静态代理是在编译期就存在的代理类
需要手动实现,耦合度高、扩展性差
动态代理是在运行期动态生成代理类
无需手写代理类,扩展性更强
Java中常见的动态代理有两种:
JDK动态代理基于接口和反射实现,要求目标类实现接口
CGLIB通过继承目标类生成子类来实现代理,不要求接口,但不能代理final类和方法
为什么 JDK 动态代理只能代理接口?
因为生成的代理类已经继承了Proxy类,Java 不支持多继承,只能通过实现接口。
动态代理和装饰器模式的区别?
代理:控制访问
装饰器:增强功能
实现方式相似,但语义不同