目录
什么叫代理
代理模式是一种比较好理解的设计模式。简单来说就是我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。
Java支持两种代理机制:
- 静态代理(手动编写代理类)
- 动态代理(运行时动态生成代理类)
静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。
上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情
//首先,我们要写出联系代理类与目标类的桥梁,接口
public interface BuyClothes {
public void clothes(String size);
}
//然后我们写出目标类,这里用ClothedFactory为例
public class ClothesFactory implements BuyClothes{
@Override
public void clothes(String size) {
System.out.println("已经为您定制好了大小为" + size +"的衣服");
}
}
//写出静态代理类
public class StaticProxy implements BuyClothes {
private ClothesFactory clothesFactory = new ClothesFactory();
//重写核心方法
@Override
public void clothes(String size) {
//代理类与目标类建立起了桥梁
clothesFactory.clothes(size);
}
}
//模拟用户方面
public class Test {
public static void main(String[] args) {
StaticProxy staticProxy = new StaticProxy();
//此处,用户访问目标类的路径:用户-->代理类StaticProxy---(以共同接口BuyClothes为中间节点)--->目标类ClothesFactory-->完成clothes()方法
staticProxy.clothes("XXXL");
}
}
优缺点
优点:
- 简单直观
- 适合代理少量方法
缺点:
- 每个目标类需手动编写代理类
- 代码冗余
动态代理
一个代理类代理多个目标类
在静态代理中,我们代理的仅仅是一个方法,但是在平时,一个类通常会有很多方法,对应的对象也会调用多个类,那么此时如果还是用静态代理,就会特别麻烦,所以就有了动态代理
JDK中提供了一个方法,可以动态的实现新增的接口,该方法是
Proxy.newProxyInstance()
动态代理在运行时动态生成代理类,无需手动编写。Java提供两种实现方式:
- JDK动态代理(基于接口)
- CGLIB动态代理(基于类继承)
JDK动态代理
核心类
java.lang.reflect.Proxy; //生成代理类
java.lang.reflect.InvocationHandler; //定义代理逻辑
JDK动态代理的实现
步骤
- 创建接口,定义目标类要完成的功能
- 创建目标类实现接口
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
- 调用目标方法
- 增强功能
- 使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型
示例
import java.lang.reflect.*;
// 1. 定义接口和目标类(同上)
// 2. 实现 InvocationHandler
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("【动态代理】方法执行完毕");
return result;
}
}
// 3. 生成代理对象
public class JdkProxyDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogHandler(target)
);
proxy.save(); // 通过代理调用
}
}
特点
- 要求:目标类必须实现接口
- 性能:反射调用稍慢,但现代JVM已优化
CGLIB动态代理
- cglib是第三方的工具库, 创建代理对象
- cglib的原理是继承, cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改
- 因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的
- 不需要目标类有接口,通过继承就可以
- 经常用于框架中,例如Spring(SpringAOP)
代理机制对比
特性 | 静态代理 | JDk动态代理 | CGLIB动态代理 |
---|---|---|---|
是否需要接口 | 是 | 是 | 否 |
生成方式 | 手动编写 | 运行时动态生成 | 运行时动态生成 |
性能 | 高 | 中等(反射调用) | 高(字节码增强) |
使用场景 | 简单、少量代理 | 基于接口的代理 | 无接口的类代理 |
总结
- 静态代理:简单但冗余,适合少量固定增强
- JDK动态代理:灵活,虚接口,适合SpringAOP等场景
- CGLIB:更高效,可代理无接口类,但需注意final限制
根据需求选择合适代理方式