Java设计模式之结构型—代理模式
Java中最常用的设计模式-CSDN博客
静态代理
“在编译期就写死代理类,实现/继承同一个接口/父类,把真实对象包一层,手动加逻辑。”
-
场景
-
对真实对象做 日志、权限、缓存 等横切增强
-
真实对象不许改动(第三方、旧代码)
-
代理类数量固定 → 适合 接口少、实现类少 的场景
代码块
// 1) 公共接口
public interface UserService {void save(String name);
}// 2) 真实对象
public class UserServiceImpl implements UserService {public void save(String name) {System.out.println("保存用户:" + name);}
}// 3) 代理类(编译期手写)
public class UserServiceProxy implements UserService {private final UserService real; // 组合真实对象public UserServiceProxy(UserService real) {this.real = real;}public void save(String name) {System.out.println("前置日志:准备保存");real.save(name); // 调用真实对象System.out.println("后置日志:保存完成");}
}// 4) 使用
UserService service = new UserServiceProxy(new UserServiceImpl());
service.save("Alice");
优缺点
优点 | 缺点 |
简单直观,无运行时开销 | 每多一个接口/实现,就要手写一个代理类 → 类爆炸 |
编译期即可检查类型 | 无法代理 未实现接口的方法 |
动态代理
Dynamic Proxy
方案 | 技术 | 代理目标 | 关键类 |
JDK 动态代理 | java.lang.reflect.Proxy + InvocationHandler | 只能代理接口 | Proxy.newProxyInstance() |
CGLIB/ByteBuddy | 字节码生成库 | 接口 + 普通类 | Enhancer.create() |
JDK 动态代理
// 订单服务接口
public interface OrderService {void create(); // 创建订单的业务方法
}// 订单服务实现类
public class OrderServiceImpl implements OrderService {public void create() {System.out.println("创建订单"); // 真正的业务逻辑}
}// 日志处理器:实现 InvocationHandler 接口,用来在真实方法前后附加日志
public class LogHandler implements InvocationHandler {private final Object target; // 被代理的“真实对象”public LogHandler(Object target) {this.target = target; // 构造时把真实对象传进来}// 每当代理对象上的任何方法被调用时,都会走到这里public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("前置日志"); // 1. 前置增强(如记录开始时间、打印参数等)Object result = method.invoke(target, args); // 2. 通过反射调用真实对象的方法System.out.println("后置日志"); // 3. 后置增强(如记录结束时间、打印返回值等)return result; // 4. 把真实方法的返回值原样返回}
}// ---------------- 使用示例 ----------------
public class Main {public static void main(String[] args) {// 1. 用 JDK 的 Proxy 工具生成一个“代理对象”OrderService proxy = (OrderService) Proxy.newProxyInstance(OrderService.class.getClassLoader(), // 类加载器:告诉 JVM 把代理类加载到哪个命名空间new Class<?>[]{OrderService.class}, // 需要实现的接口列表(可多个)new LogHandler(new OrderServiceImpl()) // 调用处理器:真正干活儿的 LogHandler);// 2. 通过代理对象调用方法proxy.create(); // 控制台会依次打印:// 前置日志// 创建订单// 后置日志}
}
public Object invoke(Object proxy, Method method, Object[] args)参数含义
-
Object proxy
这是 代理对象本身(就是Proxy.newProxyInstance()
返回的那个对象)。-
如果你在这里面再调用
proxy.toString()
、proxy.create()
之类的方法,会再次进入invoke
,极易造成无限递归,所以通常不会直接用它。 -
主要用途:在需要判断
proxy == 某个代理实例
或打印调试信息时才会用到。
-
-
Method method
本次被调用的方法对象。-
通过
method.getName()
可以知道调的是哪个方法(如create
)。 -
通过
method.getParameterTypes()
、method.getReturnType()
等可以拿到签名信息,用来做通用逻辑(例如所有get*
方法做缓存、所有save*
方法做事务等)。
-
-
Object[] args
本次方法调用时传进来的实参数组,按声明顺序排列。-
如果方法无参,它就是
null
或空数组。 -
可以通过
args[i]
读取或修改参数值,实现诸如统一参数校验、脱敏、记录日志等横切逻辑。
-
CGLIB 最简示例(可代理类)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {System.out.println("前置");Object result = proxy.invokeSuper(obj, args);System.out.println("后置");return result;
});
UserService proxy = (UserService) enhancer.create();
proxy.save("Bob");
动态代理 vs 静态代理
维度 | 动态代理 | 静态代理 |
代码量 | 1 个 InvocationHandler 即可 | 每接口/实现都要手写 |
代理范围 | 任意接口/类(CGLIB) | 只能固定接口 |
性能 | 反射调用略慢(JDK)/ ASM 接近原生(CGLIB) | 直接调用,零反射 |
生成时机 | 运行时 | 编译期 |