代理模式核心概念
代理模式核心概念
代理模式是一种结构型设计模式,通过创建一个代理对象来控制对原始对象的访问。主要分为两类:
一、静态代理 (Static Proxy)
定义:在编译期确定代理关系的模式,代理类和目标类都需要实现相同的接口。
核心特点:
- 手动编码:需要为每个目标类编写对应的代理类
- 编译时绑定:代理关系在编译期确定
- 强类型:代理类直接实现目标接口
- 无反射:直接调用目标方法,性能较高
实现步骤:
// 1. 定义接口 interface Database { void query(String sql); } // 2. 真实目标类 class MySQL implements Database { public void query(String sql) { System.out.println("执行MySQL查询: " + sql); } } // 3. 静态代理类 class DatabaseProxy implements Database { private Database target; public DatabaseProxy(Database target) { this.target = target; } public void query(String sql) { // 前置增强 System.out.println("[日志] 开始执行查询: " + sql); // 调用真实对象 target.query(sql); // 后置增强 System.out.println("[日志] 查询完成"); } } // 4. 使用代理 public class Main { public static void main(String[] args) { Database realDB = new MySQL(); Database proxy = new DatabaseProxy(realDB); proxy.query("SELECT * FROM users"); } }
输出:
[日志] 开始执行查询: SELECT * FROM users 执行MySQL查询: SELECT * FROM users [日志] 查询完成
优点:
- 代码直观,易于理解
- 编译期检查,类型安全
- 执行效率高(无反射开销)
缺点:
- 每个目标类都需要创建代理类
- 接口变更时代码需要同步修改
- 无法动态扩展功能
适用场景:
- 代理少量固定类
- 需要严格类型检查的场景
- 性能敏感的场景
二、动态代理 (Dynamic Proxy)
定义:在运行时动态创建代理对象的模式,无需提前编写代理类。
核心特点:
- 运行时生成:代理类在程序运行时动态创建
- 基于接口:JDK动态代理要求目标类必须实现接口
- 反射机制:通过反射调用目标方法
- 灵活扩展:一个代理类可代理多个目标类
1. JDK 动态代理(基于接口)
// 1. 定义接口(同上) interface Database { ... } // 2. 真实目标类(同上) class MySQL implements Database { ... } // 3. 实现InvocationHandler class LoggingHandler implements InvocationHandler { private Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[日志] 开始执行: " + method.getName()); Object result = method.invoke(target, args); System.out.println("[日志] 执行完成"); return result; } } // 4. 使用代理 public class Main { public static void main(String[] args) { Database realDB = new MySQL(); Database proxy = (Database) Proxy.newProxyInstance( Database.class.getClassLoader(), new Class[]{Database.class}, new LoggingHandler(realDB) ); proxy.query("SELECT * FROM orders"); } }
2. CGLIB 动态代理(基于继承)
// 1. 目标类(无需接口) class PaymentService { public void pay(double amount) { System.out.println("支付金额: " + amount); } } // 2. 方法拦截器 class PaymentInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("[安全校验] 开始支付"); Object result = proxy.invokeSuper(obj, args); System.out.println("[通知] 支付成功"); return result; } } // 3. 使用代理 public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PaymentService.class); enhancer.setCallback(new PaymentInterceptor()); PaymentService proxy = (PaymentService) enhancer.create(); proxy.pay(199.99); } }
输出:
[安全校验] 开始支付 支付金额: 199.99 [通知] 支付成功
优点:
- 无需编写代理类
- 支持代理多个目标类
- 功能扩展灵活
- 适应接口变化
缺点:
- JDK代理要求目标类必须实现接口
- CGLIB不能代理final类/方法
- 反射调用有性能开销
- 调试相对复杂
适用场景:
- AOP实现(如Spring)
- 远程方法调用(RPC)
- 事务管理
- 权限控制
- 日志记录
三、关键对比
特性 静态代理 动态代理 创建时机 编译期 运行时 实现方式 手动编码代理类 自动生成字节码 接口要求 需要实现相同接口 JDK代理需要接口/CGLIB不需要 性能 高(直接调用) 中(反射调用) 扩展性 差(每类需单独代理) 强(通用代理处理器) 代码复杂度 高(重复代码多) 低(集中处理) 维护成本 高(接口变更需修改) 低(自动适应) 代理类数量 与目标类数量相同 运行时动态生成 四、Spring框架中的应用
-
AOP实现:
(图片来源网络,侵删)- JDK动态代理:代理接口实现类
- CGLIB:代理无接口的类
// Spring配置强制使用CGLIB @EnableAspectJAutoProxy(proxyTargetClass = true)
-
事务管理:
@Transactional // 基于动态代理实现 public void transfer(Account from, Account to, double amount) { // ... }
-
解决循环依赖:
(图片来源网络,侵删)- 通过三级缓存存储代理对象
- 提前暴露代理对象解决依赖
-
声明式服务:
- @Cacheable 缓存代理
- @Async 异步方法代理
- @Retryable 重试代理
最佳实践:在Spring Boot 3.x中,默认优先使用CGLIB代理(通过设置spring.aop.proxy-target-class=true),因为它能代理任何类而不仅限于接口实现类。
(图片来源网络,侵删)AOP 中的代理机制详解
在 AOP(面向切面编程)中,代理是实现横切关注点(如日志、事务、安全等)的核心技术。Spring AOP 主要使用动态代理实现切面功能,下面详细解析其在 AOP 中的应用:
一、代理在 AOP 中的作用
-
解耦核心业务与横切逻辑
- 代理对象包裹原始对象(Target)
- 在方法执行前后插入增强逻辑(Advice)
// 代理执行流程 proxy.method() { beforeAdvice(); // 前置增强 target.method(); // 原始方法 afterAdvice(); // 后置增强 }
-
实现方式对比
代理类型 实现机制 在 AOP 中的应用场景 静态代理 手动编写代理类 简单场景,不常用 动态代理 运行时生成字节码 Spring AOP 默认实现
二、Spring AOP 的代理实现
1. JDK 动态代理(基于接口)
- 触发条件:目标类实现了至少一个接口
- 实现原理:
public class JdkDynamicProxy { public static Object createProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), // 关键:获取所有接口 (proxy, method, args) -> { System.out.println("[前置增强]"); Object result = method.invoke(target, args); System.out.println("[后置增强]"); return result; } ); } }
2. CGLIB 动态代理(基于继承)
- 触发条件:目标类未实现接口
- 实现原理:
public class CglibProxy { public static Object createProxy(Class targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); // 关键:设置父类 enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { System.out.println("[事务开始]"); Object result = proxy.invokeSuper(obj, args); System.out.println("[事务提交]"); return result; }); return enhancer.create(); } }
三、Spring AOP 代理工作流程
典型场景:日志记录切面
@Aspect @Component public class LoggingAspect { // 切点定义 @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} // 环绕通知(最强大的通知类型) @Around("serviceMethods()") public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); // 前置增强 System.out.println("[LOG] 进入方法: " + methodName); try { // 执行原始方法 Object result = joinPoint.proceed(); // 后置增强 System.out.println("[LOG] 方法成功: " + methodName); return result; } catch (Exception e) { // 异常增强 System.out.println("[LOG] 方法异常: " + methodName); throw e; } } }
代理执行时序:
- 容器创建目标 Bean(如 UserService)
- 检测到需要 AOP 增强
- 根据目标类选择代理方式:
- 有接口 → JDK 代理
- 无接口 → CGLIB 代理
- 生成代理对象并注入到依赖方
- 方法调用时执行增强链
四、关键特性解析
1. 代理选择策略
- Spring Boot 2.x+:默认优先使用 CGLIB
# 显式配置使用 CGLIB spring.aop.proxy-target-class=true
- 传统 Spring:按目标类是否实现接口自动选择
2. 代理限制与解决方案
问题 解决方案 自调用失效(this调用) 使用 AopContext.currentProxy() final 方法无法增强 避免对 final 方法使用 AOP 构造方法不拦截 改用初始化回调(@PostConstruct) 私有方法不拦截 Spring AOP 只拦截 public 方法 3. 性能优化建议
- 减少切点匹配复杂度:精确限定切点范围
// 优化前(低效) @Pointcut("execution(* com.example..*.*(..))") // 优化后(高效) @Pointcut("execution(public * com.example.service.*Service.*(..))")
- 避免在切面中做重型操作:如数据库访问
- 合理使用缓存:对重复计算的结果进行缓存
五、静态代理在 AOP 中的应用
虽然 Spring AOP 主要使用动态代理,但理解静态代理有助于掌握 AOP 本质:
手动实现 AOP 效果(伪代码)
// 原始类 class UserService { public void saveUser(User user) { // 业务逻辑 } } // 静态代理增强 class UserServiceProxy extends UserService { private UserService target; public UserServiceProxy(UserService target) { this.target = target; } @Override public void saveUser(User user) { // 前置增强 log.info("开始保存用户"); // 调用原始方法 target.saveUser(user); // 后置增强 log.info("用户保存成功"); } }
注意:实际 Spring AOP 不采用此方式,因为需要为每个类创建代理,无法应对复杂系统。
六、最佳实践
-
优先使用接口:方便 JDK 代理,避免 CGLIB 限制
// 推荐实现接口 public class OrderService implements IOrderService {...}
-
最小化切面范围:精确控制增强目标
// 精确到具体方法 @Pointcut("execution(* com.example.service.OrderService.createOrder(..))")
-
谨慎使用 @Around:确保调用 proceed()
@Around("pointcut()") public Object aroundAdvice(ProceedingJoinPoint pjp) { // 必须调用 pjp.proceed() return pjp.proceed(); }
-
代理类型检测:
if (AopUtils.isJdkDynamicProxy(bean)) { // JDK 代理处理 } else if (AopUtils.isCglibProxy(bean)) { // CGLIB 代理处理 }
通过合理利用代理机制,Spring AOP 实现了业务逻辑与横切关注的完美解耦,是构建可维护、可扩展系统的关键技术。
-
- 减少切点匹配复杂度:精确限定切点范围
-
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。