设计模式详解-代理模式

设计模式详解:代理模式

一、模式概述

代理模式(Proxy Pattern)是结构型设计模式中最具控制力的模式,其核心意图在于为其他对象提供一种代理以控制对这个对象的访问。这一模式在客户端与目标对象之间插入一个中间层,代理对象与目标对象实现相同的接口,对客户端透明,却在幕后承担着访问控制、延迟加载、远程通信、日志监控等横切职责。

代理模式的命名直接揭示了其本质——"代理"即代为处理、代表行事。在法律领域,律师代理当事人出庭;在商业领域,代理商代表厂商销售产品;在网络领域,代理服务器转发客户端请求。这些现实隐喻均指向同一核心特征:代理持有对被代理者的引用,对外呈现与被代理者一致的接口,对内附加额外的控制逻辑

代理模式的深层价值在于在不修改目标对象的前提下增强其行为。这一特性使其成为面向切面编程(AOP)、依赖注入容器、分布式系统基础设施的基石。Spring框架的声明式事务、@Transactional注解的魔法、MyBatis的Mapper接口实现、Dubbo的服务调用,底层均是代理模式的具体应用。

二、模式结构

代理模式包含三个核心角色,形成清晰的委托结构:

抽象主题(Subject):定义代理对象与真实对象的共同接口,客户端面向此接口编程。

真实主题(Real Subject):被代理的目标对象,实现核心业务逻辑。

代理(Proxy):实现抽象主题接口,持有对真实主题的引用。在调用真实主题的方法前后,执行额外的控制逻辑。

代理模式存在多种变体,按用途可分为:

  • 远程代理(Remote Proxy):为位于不同地址空间的对象提供本地代表,隐藏网络通信细节。
  • 虚拟代理(Virtual Proxy):延迟创建开销大的对象,在真正需要时才实例化。
  • 保护代理(Protection Proxy):基于权限控制对对象的访问,实现安全策略。
  • 智能引用(Smart Reference):在访问对象时执行额外的引用计数、加载锁定、缓存等操作。

三、深度案例:分布式金融交易系统的服务代理层

以下展示一个真实场景下的代理模式综合应用——支撑日均千万笔交易的分布式金融核心系统,涵盖远程调用、事务控制、缓存、熔断、审计等多维度代理需求。

3.1 抽象主题:账户服务接口

java
/** * 抽象主题:账户服务 * 所有代理实现与真实实现均基于此接口 */ public interface AccountService { // 查询类操作 AccountDTO getAccount(String accountNo); BigDecimal getBalance(String accountNo); List<TransactionDTO> getRecentTransactions(String accountNo, int limit); AccountStatistics getStatistics(String accountNo, DateRange range); // 变更类操作 AccountDTO openAccount(AccountOpeningRequest request); void freezeAccount(String accountNo, FreezeReason reason); void unfreezeAccount(String accountNo, String operatorId); void updateAccountInfo(String accountNo, AccountInfoUpdateRequest request); // 资金类操作(需严格事务控制) TransferResult transfer(TransferRequest request); void deposit(String accountNo, BigDecimal amount, String reference); void withdraw(String accountNo, BigDecimal amount, String reference); // 批量操作 BatchTransferResult batchTransfer(BatchTransferRequest request); List<AccountDTO> batchGetAccounts(List<String> accountNos); }

3.2 真实主题:核心账户服务实现

java
/** * 真实主题:核心账户服务 * 聚焦纯业务逻辑,无任何横切关注点 */ @Service public class CoreAccountService implements AccountService { private final AccountRepository accountRepository; private final TransactionRepository transactionRepository; private final AccountingEngine accountingEngine; private final EventPublisher eventPublisher; @Autowired public CoreAccountService(AccountRepository accountRepository, TransactionRepository transactionRepository, AccountingEngine accountingEngine, EventPublisher eventPublisher) { this.accountRepository = accountRepository; this.transactionRepository = transactionRepository; this.accountingEngine = accountingEngine; this.eventPublisher = eventPublisher; } @Override public AccountDTO getAccount(String accountNo) { Account account = accountRepository.findByAccountNo(accountNo) .orElseThrow(() -> new AccountNotFoundException(accountNo)); return AccountMapper.toDTO(account); } @Override public TransferResult transfer(TransferRequest request) { // 纯业务逻辑:借贷记账、余额校验、事务记录 Account debitAccount = accountRepository .findByAccountNoForUpdate(request.getDebitAccountNo()); Account creditAccount = accountRepository .findByAccountNoForUpdate(request.getCreditAccountNo()); // 余额校验 if (debitAccount.getAvailableBalance().compareTo(request.getAmount()) < 0) { throw new InsufficientBalanceException(request.getDebitAccountNo()); } // 执行转账 String transactionId = accountingEngine.post(request); // 发布领域事件 eventPublisher.publish(new TransferCompletedEvent( transactionId, request.getDebitAccountNo(), request.getCreditAccountNo(), request.getAmount(), request.getCurrency() )); return TransferResult.success(transactionId); } // 其余方法实现... }

3.3 远程代理:RPC服务暴露与消费

java
/** * 服务端代理:Dubbo服务暴露 * 将本地服务包装为远程可调用的代理 */ @Configuration public class AccountServiceProviderConfig { @Bean @DubboService( version = "1.0.0", timeout = 5000, retries = 0, loadbalance = "consistenthash", cluster = "failfast" ) public AccountService accountService(CoreAccountService coreService) { // 返回的代理对象被注册到Zookeeper/Nacos // 消费者通过接口名+版本号发现服务 return coreService; } } /** * 客户端代理:RPC服务消费 * 本地接口调用透明转换为网络请求 */ @Configuration public class AccountServiceConsumerConfig { @DubboReference( version = "1.0.0", check = false, mock = "return null", merger = "true" ) private AccountService accountService; // 注入的accountService实为Dubbo生成的代理 // 内部处理:服务发现、负载均衡、序列化、网络传输、超时重试 } /** * 自定义RPC代理:基于Netty的私有协议 * 展示代理模式的底层实现原理 */ public class RpcProxyFactory { private final ServiceDiscovery discovery; private final LoadBalancer loadBalancer; private final NettyClient nettyClient; private final Serializer serializer; @SuppressWarnings("unchecked") public <T> T createProxy(Class<T> interfaceClass, String serviceName) { return (T) Proxy.newProxyInstance( interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, new RpcInvocationHandler(interfaceClass, serviceName) ); } private class RpcInvocationHandler implements InvocationHandler { private final Class<?> interfaceClass; private final String serviceName; RpcInvocationHandler(Class<?> interfaceClass, String serviceName) { this.interfaceClass = interfaceClass; this.serviceName = serviceName; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1. 构建RPC请求 RpcRequest request = new RpcRequest(); request.setRequestId(UUID.randomUUID().toString()); request.setServiceName(serviceName); request.setInterfaceName(interfaceClass.getName()); request.setMethodName(method.getName()); request.setParameterTypes(method.getParameterTypes()); request.setParameters(args); // 2. 服务发现 List<ServiceInstance> instances = discovery.discover(serviceName); ServiceInstance target = loadBalancer.select(instances, request); // 3. 序列化 byte[] payload = serializer.serialize(request); // 4. 网络调用(同步阻塞,异步场景返回CompletableFuture) byte[] responseBytes = nettyClient.send(target.getAddress(), payload, 5000); // 5. 反序列化响应 RpcResponse response = serializer.deserialize(responseBytes, RpcResponse.class); if (response.isError()) { throw new RpcException(response.getErrorMessage()); } return response.getResult(); } } }

3.4 虚拟代理:延迟加载与资源优化

java
/** * 虚拟代理:账户详情的延迟加载 * 账户列表展示时无需加载完整详情,仅在需要时触发 */ public class LazyLoadingAccountService implements AccountService { private final AccountService realService; private final LoadingCache<String, AccountDTO> accountCache; // 轻量级摘要信息(立即加载) private final AccountSummaryService summaryService; public LazyLoadingAccountService(AccountService realService, AccountSummaryService summaryService) { this.realService = realService; this.summaryService = summaryService; this.accountCache = Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(Duration.ofMinutes(10)) .build(realService::getAccount); } @Override public AccountDTO getAccount(String accountNo) { // 优先从缓存获取,未命中时触发真实加载 return accountCache.get(accountNo); } @Override public List<TransactionDTO> getRecentTransactions(String accountNo, int limit) { // 虚拟代理策略:首次返回空列表并触发异步加载 // 前端通过轮询或WebSocket获取最终数据 CompletableFuture.runAsync(() -> { List<TransactionDTO> transactions = realService .getRecentTransactions(accountNo, limit); // 推送至客户端... }); return Collections.emptyList(); // 或返回占位符 } @Override public AccountStatistics getStatistics(String accountNo, DateRange range) { // 复杂统计的延迟计算:返回计算中的占位对象 return AccountStatistics.computingPlaceholder(); } // 委托给其他方法... @Override public TransferResult transfer(TransferRequest request) { return realService.transfer(request); // 资金操作不延迟 } } /** * 虚拟代理:大对象的分页加载 */ public class PagedAccountListProxy implements AccountService { private final AccountService realService; private final int pageSize; @Override public List<AccountDTO> batchGetAccounts(List<String> accountNos) { // 分批加载,避免一次性加载过多数据 List<AccountDTO> results = new ArrayList<>(); for (int i = 0; i < accountNos.size(); i += pageSize) { List<String> page = accountNos.subList(i, Math.min(i + pageSize, accountNos.size())); results.addAll(realService.batchGetAccounts(page)); // 流式返回或延迟加载下一页 if (i + pageSize < accountNos.size()) { Thread.sleep(10); // 避免压垮下游 } } return results; } // 其余方法委托... }

3.5 保护代理:权限控制与安全审计

java
/** * 保护代理:基于RBAC的访问控制 */ @Component public class SecuredAccountService implements AccountService { private final AccountService realService; private final SecurityContextHolder securityContext; private final AuditLogger auditLogger; private final PermissionEvaluator permissionEvaluator; @Autowired public SecuredAccountService(@Qualifier("coreAccountService") AccountService realService, SecurityContextHolder securityContext, AuditLogger auditLogger, PermissionEvaluator permissionEvaluator) { this.realService = realService; this.securityContext = securityContext; this.auditLogger = auditLogger; this.permissionEvaluator = permissionEvaluator; } @Override public AccountDTO getAccount(String accountNo) { // 权限校验 Authentication auth = securityContext.getAuthentication(); if (!permissionEvaluator.hasPermission(auth, accountNo, "account:read")) { throw new AccessDeniedException("无权查看账户: " + accountNo); } // 数据范围过滤:只能查看本机构账户 AccountDTO account = realService.getAccount(accountNo); if (!belongsToCurrentOrganization(account)) { throw new AccessDeniedException("账户不在管辖范围内"); } // 脱敏处理 return maskSensitiveInfo(account); } @Override public TransferResult transfer(TransferRequest request) { Authentication auth = securityContext.getAuthentication(); // 双重权限校验 if (!permissionEvaluator.hasPermission(auth, request.getDebitAccountNo(), "account:transfer:debit")) { throw new AccessDeniedException("无权从该账户转出"); } if (!permissionEvaluator.hasPermission(auth, request.getCreditAccountNo(), "account:transfer:credit")) { throw new AccessDeniedException("无权向该账户转入"); } // 金额阈值控制:大额转账需额外授权 if (request.getAmount().compareTo(new BigDecimal("1000000")) >= 0) { if (!hasLargeTransferApproval(auth, request)) { throw new AccessDeniedException("大额转账未获授权"); } } // 审计日志 auditLogger.log(AuditEvent.builder() .type(AuditEventType.TRANSFER_ATTEMPT) .operator(auth.getName()) .targetAccount(request.getDebitAccountNo()) .amount(request.getAmount()) .timestamp(Instant.now()) .build()); try { TransferResult result = realService.transfer(request); auditLogger.log(AuditEvent.builder() .type(AuditEventType.TRANSFER_SUCCESS) .operator(auth.getName()) .transactionId(result.getTransactionId()) .build()); return result; } catch (Exception e) { auditLogger.log(AuditEvent.builder() .type(AuditEventType.TRANSFER_FAILURE) .operator(auth.getName()) .errorMessage(e.getMessage()) .build()); throw e; } } @Override public void freezeAccount(String accountNo, FreezeReason reason) { // 敏感操作:需记录操作理由并通知相关方 Authentication auth = securityContext.getAuthentication(); if (!permissionEvaluator.hasPermission(auth, accountNo, "account:freeze")) { throw new AccessDeniedException("无权冻结账户"); } // 操作前确认 requireSecondaryConfirmation(auth, "freeze_account"); realService.freezeAccount(accountNo, reason); // 触发通知 notifyAccountHolder(accountNo, AccountNotificationType.FROZEN, reason); } // 辅助方法 private boolean belongsToCurrentOrganization(AccountDTO account) { String currentOrg = securityContext.getCurrentOrganizationId(); return account.getOrganizationId().equals(currentOrg); } private AccountDTO maskSensitiveInfo(AccountDTO account) { account.setIdCardNo(maskIdCard(account.getIdCardNo())); account.setMobileNo(maskMobile(account.getMobileNo())); return account; } private String maskIdCard(String idCard) { if (idCard == null || idCard.length() != 18) return idCard; return idCard.substring(0, 6) + "********" + idCard.substring(14); } private String maskMobile(String mobile) { if (mobile == null || mobile.length() != 11) return mobile; return mobile.substring(0, 3) + "****" + mobile.substring(7); } }

3.6 智能引用:引用计数与资源管理

java
/** * 智能引用代理:连接池中的连接代理 * 跟踪连接使用状态,实现自动归还与泄漏检测 */ public class PooledConnectionProxy implements Connection { private final Connection realConnection; private final ConnectionPool pool; private final AtomicInteger referenceCount = new AtomicInteger(1); private volatile boolean closed = false; private volatile long lastUsedTime; private final StackTraceElement[] checkoutStack; PooledConnectionProxy(Connection realConnection, ConnectionPool pool) { this.realConnection = realConnection; this.pool = pool; this.lastUsedTime = System.currentTimeMillis(); this.checkoutStack = Thread.currentThread().getStackTrace(); } @Override public Statement createStatement() throws SQLException { checkValid(); incrementReference(); try { return new PooledStatementProxy( realConnection.createStatement(), this); } finally { decrementReference(); } } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { checkValid(); incrementReference(); try { return new PooledPreparedStatementProxy( realConnection.prepareStatement(sql), this, sql); } finally { decrementReference(); } } @Override public void close() throws SQLException { if (closed) return; // 引用计数归零时才真正归还 if (referenceCount.decrementAndGet() == 0) { pool.returnConnection(this); closed = true; } } // 强制关闭(连接池维护线程调用) void forceClose() { closed = true; try { realConnection.close(); } catch (SQLException e) { // 记录异常但继续 } } // 连接泄漏检测 boolean isLeaked(long maxIdleTime) { return referenceCount.get() > 0 && System.currentTimeMillis() - lastUsedTime > maxIdleTime; } StackTraceElement[] getCheckoutStack() { return checkoutStack; } private void checkValid() { if (closed) { throw new ConnectionClosedException("连接已关闭,可能已归还至连接池"); } } private void incrementReference() { referenceCount.incrementAndGet(); lastUsedTime = System.currentTimeMillis(); } private void decrementReference() { referenceCount.decrementAndGet(); } // 其余Connection方法委托给realConnection... }

3.7 动态代理:AOP与声明式事务

java
/** * JDK动态代理:通用服务拦截器 */ public class ServiceInterceptorProxy implements InvocationHandler { private final Object target; private final List<ServiceInterceptor> interceptors; public ServiceInterceptorProxy(Object target, List<ServiceInterceptor> interceptors) { this.target = target; this.interceptors = interceptors; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { InvocationContext context = new InvocationContext(target, method, args); // 前置拦截 for (ServiceInterceptor interceptor : interceptors) { interceptor.before(context); if (context.isShortCircuited()) { return context.getReturnValue(); } } try { // 执行目标方法 Object result = method.invoke(target, args); context.setReturnValue(result); // 后置拦截 for (int i = interceptors.size() - 1; i >= 0; i--) { interceptors.get(i).afterReturning(context); } return result; } catch (InvocationTargetException e) { Throwable cause = e.getTargetException(); context.setException(cause); // 异常拦截 for (int i = interceptors.size() - 1; i >= 0; i--) { interceptors.get(i).afterThrowing(context); } throw cause; } finally { // 最终拦截 for (int i = interceptors.size() - 1; i >= 0; i--) { interceptors.get(i).afterFinally(context); } } } } /** * CGLIB代理:无接口类的代理实现 */ public class CglibTransactionProxy implements MethodInterceptor { private final PlatformTransactionManager txManager; private final TransactionAttributeSource attributeSource; @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 获取事务属性 TransactionAttribute txAttr = attributeSource .getTransactionAttribute(method, obj.getClass()); if (txAttr == null) { // 无事务要求,直接执行 return proxy.invokeSuper(obj, args); } TransactionStatus status = txManager.getTransaction(txAttr); try { Object result = proxy.invokeSuper(obj, args); txManager.commit(status); return result; } catch (RuntimeException e) { if (txAttr.rollbackOn(e)) { txManager.rollback(status); } else { txManager.commit(status); } throw e; } } } /** * Spring AOP实战:声明式事务与缓存 */ @Service public class AopAccountService { @Autowired private AccountRepository accountRepository; // 事务代理:方法进入时开启事务,成功提交,异常回滚 @Transactional( isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, timeout = 30, rollbackFor = {BusinessException.class, SystemException.class} ) public TransferResult transfer(TransferRequest request) { // 纯业务逻辑,无任何事务控制代码 Account debit = accountRepository.findByAccountNoForUpdate( request.getDebitAccountNo()); Account credit = accountRepository.findByAccountNoForUpdate( request.getCreditAccountNo()); debit.debit(request.getAmount()); credit.credit(request.getAmount()); accountRepository.save(debit); accountRepository.save(credit); return TransferResult.success(generateTransactionId()); } // 缓存代理:结果自动存入Redis,下次直接返回 @Cacheable( value = "account", key = "#accountNo", unless = "#result == null", sync = true // 防止缓存击穿 ) public AccountDTO getAccount(String accountNo) { return accountRepository.findByAccountNo(accountNo) .map(AccountMapper::toDTO) .orElse(null); } // 缓存更新:数据变更时失效缓存 @CacheEvict(value = "account", key = "#accountNo") @Transactional public void updateAccountInfo(String accountNo, AccountInfoUpdateRequest request) { // 更新逻辑... } // 多重代理组合:缓存 + 事务 + 审计 @Cached @Transactional(readOnly = true) @AuditLog(operation = "QUERY_STATISTICS") public AccountStatistics getStatistics(String accountNo, DateRange range) { // 复杂统计查询... return statistics; } }

3.8 代理链装配:Spring配置

java
@Configuration public class AccountServiceProxyConfig { @Bean @Primary // 注入时优先选择代理 public AccountService accountService( CoreAccountService coreService, SecurityContextHolder securityContext, AuditLogger auditLogger, PermissionEvaluator permissionEvaluator, PlatformTransactionManager txManager, CacheManager cacheManager) { // 构建代理链:最内层是核心业务,逐层包装 // 第一层:核心业务 AccountService service = coreService; // 第二层:安全保护代理 service = new SecuredAccountService(service, securityContext, auditLogger, permissionEvaluator); // 第三层:虚拟代理(延迟加载) service = new LazyLoadingAccountService(service, summaryService); // 第四层:智能引用(连接池管理,仅适用于资源操作) // service = new PooledServiceProxy(service, servicePool); // Spring AOP代理(声明式事务、缓存、审计) ProxyFactory factory = new ProxyFactory(service); factory.addInterface(AccountService.class); factory.addAdvice(new TransactionInterceptor(txManager, new AnnotationTransactionAttributeSource())); factory.addAdvice(new CacheInterceptor(cacheManager)); return (AccountService) factory.getProxy(); } }

四、代理模式的高级主题

4.1 反向代理与负载均衡

java
/** * 反向代理:基于Netty的API网关核心 */ public class ReverseProxyHandler extends ChannelInboundHandlerAdapter { private final ServiceRegistry registry; private final LoadBalancer loadBalancer; private final CircuitBreakerRegistry cbRegistry; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { FullHttpRequest request = (FullHttpRequest) msg; // 路由解析 Route route = registry.match(request.uri()); // 服务发现 List<ServiceInstance> instances = registry.discover(route.getServiceId()); // 负载均衡 ServiceInstance target = loadBalancer.select(instances, request); // 熔断检查 CircuitBreaker cb = cbRegistry.circuitBreaker(route.getServiceId()); if (cb.getState() == CircuitBreaker.State.OPEN) { writeServiceUnavailable(ctx, request); return; } // 构建转发请求 HttpRequest forwardRequest = createForwardRequest(request, target); // 建立与后端服务的连接并转发 connectAndForward(target, forwardRequest) .addListener(future -> { if (future.isSuccess()) { cb.recordSuccess(); } else { cb.recordFailure(); } }); } }

4.2 响应式代理:Project Reactor

java
/** * 响应式代理:非阻塞的服务调用包装 */ public class ReactiveAccountServiceProxy implements ReactiveAccountService { private final AccountService blockingService; private final Scheduler scheduler; public ReactiveAccountServiceProxy(AccountService blockingService, Scheduler scheduler) { this.blockingService = blockingService; this.scheduler = scheduler; } @Override public Mono<AccountDTO> getAccount(String accountNo) { return Mono.fromCallable(() -> blockingService.getAccount(accountNo)) .subscribeOn(scheduler) // 隔离到专用线程池 .timeout(Duration.ofSeconds(5)) .retryWhen(Retry.backoff(3, Duration.ofMillis(100))) .onErrorResume(AccountNotFoundException.class, e -> Mono.error(new ReactiveNotFoundException(e))); } @Override public Mono<TransferResult> transfer(TransferRequest request) { return Mono.fromCallable(() -> blockingService.transfer(request)) .subscribeOn(scheduler) .flatMap(result -> { // 转账成功后触发后续异步操作 return notifySettlement(result) .thenReturn(result); }); } }

五、代理模式与相关模式的辨析

代理 vs 装饰器:代理控制访问,装饰器增强功能。代理通常由客户端不可见的方式创建(工厂、容器),装饰器常由客户端显式组装。代理代表真实对象,装饰器包装组件对象。

代理 vs 适配器:代理保持相同接口,适配器转换接口。代理用于控制访问,适配器用于解决不兼容。

代理 vs 外观:代理代表单一对象,外观封装整个子系统。代理与目标对象实现相同接口,外观提供简化的新接口。

六、设计陷阱与规避策略

陷阱一:代理链过长导致性能损耗

每层代理引入方法调用开销,深度嵌套可能影响性能。解决方案:使用字节码生成(CGLIB、ByteBuddy)减少反射开销,或在热点路径提供"裸调"快捷方式。

陷阱二:自调用绕过代理

Spring AOP中,同类方法调用不走代理,导致事务、缓存注解失效。解决方案:注入自身代理、使用AspectJ编译时织入、或重构代码避免自调用。

陷阱三:代理对象的身份混淆

proxy instanceof RealSubject返回false,proxy.getClass()不等于RealSubject.class。解决方案:基于接口编程,避免依赖具体类;必要时使用AopContext获取目标对象。

七、结语

代理模式是软件设计中最具实用价值的模式之一,它以一种近乎隐形的方式,在对象访问的必经之路上织入控制逻辑。从远程通信到事务管理,从安全控制到性能优化,代理模式支撑起现代分布式系统的半壁江山。理解其静态代理、动态代理、字节码生成的实现机制,掌握代理链的装配与调试技巧,警惕自调用、身份混淆等工程陷阱,是每一位Java开发者进阶的必修课。代理模式的精髓在于控制的透明化——让增强无处不在,又让增强无迹可寻。

返回知识中心