1. 代理模式的核心价值与应用场景
代理模式(Proxy Pattern)是结构型设计模式中最常用的模式之一,它的核心价值在于:在不修改原始类代码的前提下,通过引入代理对象来控制对原始对象的访问。这种控制可以表现为多种形式,比如权限校验、性能监控、缓存处理、延迟加载等。
1.1 为什么需要代理模式
在实际开发中,我们经常会遇到这样的场景:一个类已经实现了核心业务逻辑,但现在需要在不修改这个类的前提下,增加一些额外的功能。比如:
- 在方法调用前后添加日志记录
- 对敏感方法的访问进行权限控制
- 为耗时操作添加缓存机制
- 实现远程方法的本地调用(RPC)
- 对大数据对象进行延迟加载
如果直接修改原始类代码来实现这些功能,会违反开闭原则(对扩展开放,对修改关闭)。代理模式正是解决这类问题的优雅方案。
1.2 代理模式的典型应用
代理模式在现实开发中有许多经典应用:
Spring AOP:Spring框架通过动态代理实现了面向切面编程,可以在不修改业务代码的情况下实现事务管理、日志记录等功能。
MyBatis:MyBatis的Mapper接口就是通过动态代理实现的,开发者只需要定义接口,MyBatis会在运行时生成代理对象。
RPC框架:远程服务调用通常通过代理模式实现,客户端调用本地代理对象的方法,代理对象负责将调用转发到远程服务。
Hibernate延迟加载:当访问关联对象时,Hibernate返回的是一个代理对象,只有真正访问数据时才会去数据库查询。
2. 代理模式的实现方式
代理模式主要有两种实现方式:静态代理和动态代理。它们各有优缺点,适用于不同的场景。
2.1 静态代理实现详解
静态代理是最简单的代理实现方式,它的特点是:代理类在编译期就已经确定。下面我们通过一个完整的例子来演示静态代理的实现。
// 1. 定义业务接口 public interface UserService { void addUser(String username); void deleteUser(String username); } // 2. 实现业务接口(真实对象) public class UserServiceImpl implements UserService { @Override public void addUser(String username) { System.out.println("添加用户: " + username); } @Override public void deleteUser(String username) { System.out.println("删除用户: " + username); } } // 3. 创建代理类 public class UserServiceProxy implements UserService { private UserService target; // 持有真实对象的引用 public UserServiceProxy(UserService target) { this.target = target; } @Override public void addUser(String username) { System.out.println("【代理】准备添加用户"); target.addUser(username); // 调用真实对象的方法 System.out.println("【代理】用户添加完成"); } @Override public void deleteUser(String username) { System.out.println("【代理】准备删除用户"); target.deleteUser(username); System.out.println("【代理】用户删除完成"); } } // 4. 客户端使用 public class Client { public static void main(String[] args) { UserService realService = new UserServiceImpl(); UserService proxy = new UserServiceProxy(realService); proxy.addUser("张三"); proxy.deleteUser("李四"); } }静态代理的优缺点分析:
优点:
- 实现简单,容易理解
- 在不修改目标对象的前提下扩展功能
- 代理类和目标类分离,符合单一职责原则
缺点:
- 每个代理类只能为一个接口服务,如果有多个接口需要代理,就需要创建多个代理类
- 接口一旦增加方法,目标对象和代理对象都需要维护
- 代理类代码冗余,多个代理类中可能有相似的增强逻辑
2.2 动态代理实现详解
动态代理解决了静态代理的主要缺点,它可以在运行时动态生成代理类,而不需要为每个目标类都手动创建代理类。Java中主要通过java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler来实现动态代理。
2.2.1 JDK动态代理实现
// 1. 定义业务接口(同上) public interface UserService { void addUser(String username); void deleteUser(String username); } // 2. 实现业务接口(同上) public class UserServiceImpl implements UserService { @Override public void addUser(String username) { System.out.println("添加用户: " + username); } @Override public void deleteUser(String username) { System.out.println("删除用户: " + username); } } // 3. 实现InvocationHandler public class LoggingHandler implements InvocationHandler { private Object target; // 真实对象 public LoggingHandler(Object target) { this.target = target; } // 创建代理对象 public static Object createProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LoggingHandler(target) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【动态代理】调用方法: " + method.getName()); long start = System.currentTimeMillis(); Object result = method.invoke(target, args); // 调用真实对象的方法 long end = System.currentTimeMillis(); System.out.println("【动态代理】方法执行时间: " + (end - start) + "ms"); return result; } } // 4. 客户端使用 public class Client { public static void main(String[] args) { UserService realService = new UserServiceImpl(); UserService proxy = (UserService) LoggingHandler.createProxy(realService); proxy.addUser("王五"); proxy.deleteUser("赵六"); } }2.2.2 JDK动态代理原理分析
JDK动态代理的核心是Proxy.newProxyInstance()方法,它会动态生成一个代理类,这个代理类具有以下特点:
- 继承
java.lang.reflect.Proxy类 - 实现被代理的接口
- 所有方法调用都会转发到
InvocationHandler.invoke()方法
动态生成的代理类大致如下(通过反编译可以查看):
public final class $Proxy0 extends Proxy implements UserService { private static Method m1; private static Method m2; private static Method m3; private static Method m4; static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("UserService").getMethod("addUser", Class.forName("java.lang.String")); m4 = Class.forName("UserService").getMethod("deleteUser", Class.forName("java.lang.String")); } catch (Exception e) { // 异常处理 } } public $Proxy0(InvocationHandler h) { super(h); } public final void addUser(String var1) { try { super.h.invoke(this, m3, new Object[]{var1}); } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void deleteUser(String var1) { try { super.h.invoke(this, m4, new Object[]{var1}); } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } // equals、hashCode、toString等方法 }2.2.3 JDK动态代理的局限性
JDK动态代理有一个重要限制:它只能代理接口。这是因为Java不支持多重继承,而生成的代理类已经继承了Proxy类,无法再继承其他类。如果要代理没有实现接口的类,就需要使用CGLIB。
2.3 CGLIB动态代理
CGLIB是一个强大的高性能代码生成库,它可以在运行时扩展Java类和实现接口。与JDK动态代理不同,CGLIB不需要目标类实现接口。
2.3.1 CGLIB实现示例
// 1. 目标类(不需要实现接口) public class UserService { public void addUser(String username) { System.out.println("添加用户: " + username); } public void deleteUser(String username) { System.out.println("删除用户: " + username); } } // 2. 实现MethodInterceptor public class LogInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("【CGLIB代理】调用方法: " + method.getName()); long start = System.currentTimeMillis(); Object result = proxy.invokeSuper(obj, args); // 调用父类方法 long end = System.currentTimeMillis(); System.out.println("【CGLIB代理】方法执行时间: " + (end - start) + "ms"); return result; } // 创建代理对象 public static Object createProxy(Class<?> targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(new LogInterceptor()); return enhancer.create(); } } // 3. 客户端使用 public class Client { public static void main(String[] args) { UserService proxy = (UserService) LogInterceptor.createProxy(UserService.class); proxy.addUser("钱七"); proxy.deleteUser("孙八"); } }2.3.2 CGLIB原理分析
CGLIB通过继承目标类来创建代理对象,它使用字节码处理框架ASM来转换字节码并生成新的类。代理类会重写目标类的非final方法,并在方法中调用MethodInterceptor.intercept()方法。
CGLIB生成的代理类大致如下:
public class UserService$$EnhancerByCGLIB$$12345678 extends UserService { private MethodInterceptor interceptor; public void addUser(String username) { MethodProxy methodProxy = ...; // 获取方法代理 interceptor.intercept(this, UserService.class.getMethod("addUser", String.class), new Object[]{username}, methodProxy); } // 其他方法... }2.3.3 CGLIB的特点
- 可以代理普通类,不需要实现接口
- 通过继承方式实现代理,所以不能代理final类或final方法
- 性能通常比JDK动态代理高,但创建代理对象的速度较慢
- 会生成三个class文件,体积较大
3. 代理模式的高级应用
3.1 Spring AOP中的代理选择
Spring AOP默认使用JDK动态代理,如果目标对象没有实现接口,则使用CGLIB。可以通过配置强制使用CGLIB:
<aop:config proxy-target-class="true"> <!-- 其他配置 --> </aop:config>或者在注解驱动方式下:
@EnableAspectJAutoProxy(proxyTargetClass = true)3.2 代理模式在RPC中的应用
RPC(远程过程调用)框架通常使用代理模式来实现透明化的远程调用。客户端调用本地代理对象的方法,代理对象负责将方法调用信息序列化并通过网络发送到服务端,服务端执行方法后将结果返回。
// 定义服务接口 public interface OrderService { Order getOrderById(long id); } // 客户端使用 OrderService orderService = Proxy.newProxyInstance( OrderService.class.getClassLoader(), new Class[]{OrderService.class}, new RpcInvocationHandler("order-service") ); Order order = orderService.getOrderById(123);3.3 MyBatis中的Mapper代理
MyBatis的Mapper接口不需要实现类,而是通过动态代理创建实现:
public class MapperProxy<T> implements InvocationHandler { private final SqlSession sqlSession; private final Class<T> mapperInterface; public static <T> T newInstance(SqlSession sqlSession, Class<T> mapperInterface) { return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[]{mapperInterface}, new MapperProxy(sqlSession, mapperInterface) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 根据方法名和参数执行SQL操作 String statement = mapperInterface.getName() + "." + method.getName(); return sqlSession.selectOne(statement, args); } }4. 代理模式的性能优化
4.1 代理对象的缓存
创建代理对象是一个相对耗时的操作,特别是CGLIB代理。为了提高性能,应该缓存已经创建的代理对象。
public class ProxyFactory { private static final Map<Class<?>, Object> proxyCache = new ConcurrentHashMap<>(); public static <T> T getProxy(Class<T> targetClass) { return (T) proxyCache.computeIfAbsent(targetClass, clazz -> { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(new LogInterceptor()); return enhancer.create(); }); } }4.2 选择性代理
不是所有方法都需要代理,可以在InvocationHandler.invoke()或MethodInterceptor.intercept()中根据方法名决定是否执行代理逻辑。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().startsWith("get")) { // 不代理getter方法 return method.invoke(target, args); } // 执行代理逻辑 System.out.println("【代理】调用方法: " + method.getName()); return method.invoke(target, args); }4.3 代理链模式
多个代理可以形成链式调用,每个代理处理不同的横切关注点(如日志、事务、缓存等)。
public class ProxyChain implements InvocationHandler { private Object target; private List<InvocationHandler> handlers; public static Object createProxy(Object target, List<InvocationHandler> handlers) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new ProxyChain(target, handlers) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (handlers.isEmpty()) { return method.invoke(target, args); } InvocationHandler handler = handlers.get(0); ProxyChain nextChain = new ProxyChain(target, handlers.subList(1, handlers.size())); return handler.invoke(proxy, method, args, nextChain); } } public interface ChainableInvocationHandler extends InvocationHandler { Object invoke(Object proxy, Method method, Object[] args, InvocationHandler next) throws Throwable; }