在Java中,代理模式用于为对象提供一种间接访问方式,常用于增强功能(如日志、事务等)。代理分为静态代理 和动态代理,两者实现方式和原理不同。
一、静态代理
实现过程
-
定义公共接口
代理类和被代理类需实现同一接口。
java
csharppublic interface UserService { void save(); }
-
实现被代理类(真实对象)
java
typescriptpublic class UserServiceImpl implements UserService { @Override public void save() { System.out.println("保存用户数据"); } }
-
实现代理类
代理类持有被代理对象的引用,并在方法调用前后添加增强逻辑。
java
csharppublic class UserServiceProxy implements UserService { private final UserService target; // 持有真实对象 public UserServiceProxy(UserService target) { this.target = target; } @Override public void save() { System.out.println("前置增强(如日志)"); target.save(); // 调用真实对象的方法 System.out.println("后置增强(如事务提交)"); } }
-
使用代理
java
javapublic static void main(String[] args) { UserService realService = new UserServiceImpl(); UserService proxy = new UserServiceProxy(realService); proxy.save(); // 通过代理调用方法 }
原理
- 编译时绑定:代理类在编译期已确定,需手动编写代码。
- 直接调用:代理类通过组合持有真实对象,显式调用其方法。
- 缺点:每个被代理类需单独编写代理类,代码冗余。
二、动态代理
实现过程(基于JDK内置Proxy类)
-
定义接口(同上)
java
csharppublic interface UserService { void save(); }
-
实现被代理类(同上)
java
javapublic class UserServiceImpl implements UserService { ... }
-
实现InvocationHandler
定义代理逻辑的通用处理器。
java
typescriptpublic class LogHandler implements InvocationHandler { private final 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("后置增强"); return result; } }
-
动态生成代理对象
使用
Proxy.newProxyInstance
创建代理实例。java
scsspublic static void main(String[] args) { UserService realService = new UserServiceImpl(); // 动态生成代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( realService.getClass().getClassLoader(), // 类加载器 realService.getClass().getInterfaces(), // 实现的接口 new LogHandler(realService) // InvocationHandler ); proxy.save(); // 调用代理方法 }
原理
-
运行时动态生成代理类
- JDK的
Proxy
类在运行时生成代理类的字节码(类名通常为$Proxy0
)。 - 通过
sun.misc.ProxyGenerator
生成字节码,并加载到JVM。
- JDK的
-
代理类结构
生成的代理类继承
Proxy
并实现目标接口:java
scalapublic final class $Proxy0 extends Proxy implements UserService { public $Proxy0(InvocationHandler h) { super(h); } @Override public void save() { super.h.invoke(this, // 调用InvocationHandler的invoke方法 UserService.class.getMethod("save"), null); } }
关键点
- JDK动态代理限制 :只能代理接口 ,不能代理类(因Java单继承,代理类已继承
Proxy
)。 - 性能开销:反射调用方法比直接调用略慢(但现代JVM已优化)。
- 替代方案 :若需代理类而非接口,可用CGLIB(通过继承实现代理)。
三、对比总结
特性 | 静态代理 | 动态代理(JDK) |
---|---|---|
实现时机 | 编译期(手动编写代理类) | 运行期(自动生成代理类) |
代码冗余 | 每个被代理类需单独编写代理类 | 通用(一个处理器处理多个类) |
灵活性 | 低(修改需重新编译) | 高(动态配置) |
代理对象类型 | 类或接口 | 仅接口 |
性能 | 无反射开销(直接调用) | 有反射开销(但可接受) |
典型应用 | 简单场景、少量代理 | Spring AOP、RPC框架等 |
四、动态代理底层原理(字节码层面)
- 生成代理类字节码
Proxy.newProxyInstance()
调用ProxyGenerator.generateProxyClass()
生成字节码(可通过-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
导出.class文件)。 - 加载代理类
通过ClassLoader
将字节码加载到JVM。 - 实例化代理对象
利用反射创建代理实例,构造器传入InvocationHandler
。 - 方法调用转发
代理类的方法调用全部委托给InvocationHandler.invoke()
,由它决定增强逻辑和真实对象的调用。
五、扩展:CGLIB动态代理
若需代理类(非接口),可用CGLIB库:
java
typescript
// 1. 实现MethodInterceptor
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置增强");
Object result = proxy.invokeSuper(obj, args); // 调用父类(真实对象)方法
System.out.println("后置增强");
return result;
}
}
// 2. 生成代理对象
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class); // 设置被代理类
enhancer.setCallback(new CglibProxy()); // 设置回调
UserService proxy = (UserService) enhancer.create(); // 创建代理
proxy.save();
}
原理:通过ASM库生成被代理类的子类,重写方法并插入增强逻辑。
通过理解静态代理与动态代理的实现和原理,可灵活选择适合场景的代理方式,高效实现功能增强和解耦。