资讯详情

深入解析代理模式:静态与动态代理实现原理

📅 2026/11/2 8:53:14 | 华诺云谱 👁 阅读
深入解析代理模式:静态与动态代理实现原理
1. 代理模式的核心价值与应用场景代理模式Proxy Pattern是结构型设计模式中最常用的模式之一它的核心价值在于在不修改原始类代码的前提下通过引入代理对象来控制对原始对象的访问。这种控制可以表现为多种形式比如权限校验、性能监控、缓存处理、延迟加载等。1.1 为什么需要代理模式在实际开发中我们经常会遇到这样的场景一个类已经实现了核心业务逻辑但现在需要在不修改这个类的前提下增加一些额外的功能。比如在方法调用前后添加日志记录对敏感方法的访问进行权限控制为耗时操作添加缓存机制实现远程方法的本地调用RPC对大数据对象进行延迟加载如果直接修改原始类代码来实现这些功能会违反开闭原则对扩展开放对修改关闭。代理模式正是解决这类问题的优雅方案。1.2 代理模式的典型应用代理模式在现实开发中有许多经典应用Spring AOPSpring框架通过动态代理实现了面向切面编程可以在不修改业务代码的情况下实现事务管理、日志记录等功能。MyBatisMyBatis的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。可以通过配置强制使用CGLIBaop:config proxy-target-classtrue !-- 其他配置 -- /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 MapperProxyT implements InvocationHandler { private final SqlSession sqlSession; private final ClassT mapperInterface; public static T T newInstance(SqlSession sqlSession, ClassT 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 MapClass?, Object proxyCache new ConcurrentHashMap(); public static T T getProxy(ClassT 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 ListInvocationHandler handlers; public static Object createProxy(Object target, ListInvocationHandler 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; }
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑