🎭 代理模式(Proxy Pattern):控制对象访问的智能替身
✨ 模式简介
代理模式是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问。就像明星的经纪人——外界不直接接触明星本人,而是通过经纪人来安排各种事务。
📦 应用场景
远程代理(Remote Proxy):为远程对象提供本地代表(如RMI)
虚拟代理(Virtual Proxy):延迟加载大资源对象(如图片懒加载)
保护代理(Protection Proxy):控制对敏感对象的访问权限
智能引用(Smart Reference):在访问对象时执行额外操作(如引用计数)
Spring AOP的动态代理
MyBatis的Mapper接口代理
🧠 核心实现思路
Subject(抽象主题):定义真实主题和代理的共同接口
RealSubject(真实主题):业务逻辑的实际执行者
Proxy(代理):持有真实主题的引用,控制对它的访问
🧱 代理模式的三种实现方式
1. 静态代理(编译时确定)
// 抽象主题
interface Image {
void display();
}
// 真实主题
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
// 代理类
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 延迟加载
}
realImage.display();
}
}
// 使用
Image image = new ProxyImage("test.jpg");
image.display(); // 第一次访问时加载真实图片
2. JDK动态代理(运行时生成)
// 抽象主题
interface UserService {
void addUser(String name);
}
// 真实主题
class UserServiceImpl implements UserService {
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
// 调用处理器
class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【日志】调用方法:" + method.getName());
return method.invoke(target, args);
}
}
// 使用
UserService realService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new LogInvocationHandler(realService)
);
proxy.addUser("张三");
3. CGLIB动态代理(无需接口)
// 真实类(无需实现接口)
class ProductService {
public void saveProduct(String name) {
System.out.println("保存商品:" + name);
}
}
// 方法拦截器
class AuthInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("【权限校验】");
return proxy.invokeSuper(obj, args);
}
}
// 使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
enhancer.setCallback(new AuthInterceptor());
ProductService proxy = (ProductService) enhancer.create();
proxy.saveProduct("手机");
💎 最佳实践推荐
Spring AOP 风格代理
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法调用前:" + joinPoint.getSignature());
Object result = joinPoint.proceed();
System.out.println("方法调用后");
return result;
}
}
MyBatis Mapper 代理
// 接口定义
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUser(int id);
}
// Spring中自动生成代理
@Autowired
private UserMapper userMapper; // MyBatis动态创建的代理实例
💣 常见问题与解决方案
问题1:JDK代理 vs CGLIB代理?
✅ 解决方案:
JDK代理:基于接口(Spring默认,性能较好)
CGLIB代理:基于继承(可代理普通类,创建稍慢)
问题2:如何选择代理方式?
✅ 决策树:
有接口吗?
├─ 是 → 使用JDK动态代理
└─ 否 → 使用CGLIB代理
问题3:代理对象调试困难?
✅ 解决方案:
使用
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")
保存代理类调试时关注
InvocationHandler.invoke()
方法
📊 模式对比
📚 实际应用案例
Spring事务管理
@Transactional // 基于代理实现 public void transferMoney() { ... }
RPC框架客户端
@Reference // Dubbo生成的远程服务代理 private UserService userService;
Hibernate延迟加载
User user = session.load(User.class, 1L); // 返回代理对象 user.getName(); // 实际触发数据库查询
🎯 总结建议
优先选择:Spring AOP(结合注解最简洁)
性能优化:对高频访问方法考虑静态代理
设计原则:符合开闭原则(新增功能不修改原类)
避免滥用:简单场景不必使用代理
注意陷阱:自调用方法不会被代理拦截
🛠️ 工具推荐:
Arthas:动态查看运行时的代理类
Byte Buddy:新一代字节码生成库