Spring IOC 与 AOP 理解与使用
一、IOC控制反转详解1.1 什么是 IOC控制反转Inversion of ControlIoC是一种设计原则它将对象的创建和管理权从程序代码转移给外部容器如 Spring 容器从而降低代码之间的耦合度 。传统方式 vs IOC 方式传统方式高耦合public class UserService { // 直接创建依赖对象耦合度高 private UserRepository userRepository new UserRepositoryImpl(); }IOC 方式低耦合public class UserService { private UserRepository userRepository; // 通过构造函数注入依赖 public UserService(UserRepository userRepository) { this.userRepository userRepository; } }1.2 依赖注入DI的三种方式注入方式示例特点构造函数注入public UserService(UserRepo repo)推荐方式依赖不可变测试友好Setter 注入public void setUserRepo(UserRepo repo)适用于可选依赖字段注入Autowired private UserRepo repo不推荐测试困难1.3 Spring IOC 容器使用XML 配置方式!-- applicationContext.xml -- bean iduserRepository classcom.example.UserRepositoryImpl/ bean iduserService classcom.example.UserService constructor-arg refuserRepository/ /bean注解配置方式推荐Configuration public class AppConfig { Bean public UserRepository userRepository() { return new UserRepositoryImpl(); } Bean public UserService userService(UserRepository userRepository) { return new UserService(userRepository); } } // 使用容器 ApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class); UserService userService context.getBean(UserService.class);1.4 IOC 的核心优势降低耦合组件不直接依赖具体实现便于替换易于测试可以轻松注入 Mock 对象进行单元测试集中管理统一管理对象的生命周期和依赖关系灵活配置运行时决定具体实现支持多环境切换二、AOP面向切面编程详解2.1 什么是 AOP面向切面编程Aspect-Oriented Programming是一种编程范式它将横切关注点如日志、事务、安全等从核心业务逻辑中分离出来形成独立的切面从而提高代码的模块化程度。2.2 AOP 核心概念术语说明Aspect切面封装横切关注点的模块是一个类Join Point连接点程序执行过程中的特定点如方法调用Pointcut切点匹配连接点的表达式定义哪些方法会被拦截Advice通知切面在特定连接点执行的操作Weaving织入将切面应用到目标对象的过程2.3 五种通知类型类型注解执行时机前置通知Before方法执行前后置通知AfterReturning方法成功返回后异常通知AfterThrowing方法抛出异常后最终通知After方法执行后无论成功/失败环绕通知Around包围方法执行最强大可控制是否执行目标方法2.4 Spring AOP 完整示例步骤 1添加依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency步骤 2定义切面Aspect Component public class LoggingAspect { // 定义切入点匹配 service 包下所有方法 Pointcut(execution(* com.example.service.*.*(..))) private void serviceMethods() {} // 前置通知记录方法调用 Before(serviceMethods()) public void logMethodCall(JoinPoint jp) { String methodName jp.getSignature().getName(); Object[] args jp.getArgs(); System.out.println([日志] 调用方法: methodName , 参数: Arrays.toString(args)); } // 环绕通知计算方法执行时间 Around(serviceMethods()) public Object measureExecutionTime(ProceedingJoinPoint pjp) throws Throwable { long start System.currentTimeMillis(); // 执行目标方法 Object result pjp.proceed(); long duration System.currentTimeMillis() - start; System.out.println([性能] pjp.getSignature() 执行时间: duration ms); return result; } // 后置返回通知记录返回值 AfterReturning(pointcut serviceMethods(), returning result) public void logReturn(JoinPoint jp, Object result) { System.out.println([日志] 方法返回: result); } // 异常通知记录异常 AfterThrowing(pointcut serviceMethods(), throwing ex) public void logException(JoinPoint jp, Exception ex) { System.err.println([错误] 方法执行异常: ex.getMessage()); } }步骤 3启用 AOPConfiguration EnableAspectJAutoProxy ComponentScan(com.example) public class AppConfig { }2.5 切点表达式详解// 匹配指定包下所有类的所有方法 Pointcut(execution(* com.example.service.*.*(..))) // 匹配所有 public 方法 Pointcut(execution(public * *(..))) // 匹配所有以 get 开头的方法 Pointcut(execution(* get*(..))) // 匹配指定类中的所有方法 Pointcut(within(com.example.service.UserService)) // 匹配带有特定注解的方法 Pointcut(annotation(com.example.annotation.Loggable)) // 组合切点 Pointcut(serviceMethods() !excludeMethods())三、IOC 与 AOP 的关系3.1 协作关系IOC 容器管理 AOP 代理对象的创建AOP 增强 IOC 容器中的 Bean 功能Spring AOP 基于 Spring IOC 工作AOP 代理对象由 IOC 容器管理3.2 设计互补特性IOCAOP解决问题对象创建和依赖管理横切关注点模块化实现方式依赖注入动态代理关注点对象生命周期方法增强四、实战应用场景4.1 声明式事务管理IOC AOPService public class OrderService { Autowired private OrderRepository orderRepository; Autowired private PaymentService paymentService; Transactional // AOP 实现的事务管理 public void placeOrder(Order order) { orderRepository.save(order); paymentService.processPayment(order); // 任意步骤异常事务自动回滚 } } // 配置事务管理器 Configuration EnableTransactionManagement public class PersistenceConfig { Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }4.2 自定义注解 AOP 实现权限控制// 1. 定义权限注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequiredPermission { String[] value(); } // 2. 实现权限切面 Aspect Component public class PermissionAspect { Autowired private AuthService authService; Around(annotation(requiredPermission)) public Object checkPermission(ProceedingJoinPoint pjp, RequiredPermission requiredPermission) throws Throwable { String[] permissions requiredPermission.value(); if (authService.hasAnyPermission(permissions)) { return pjp.proceed(); } else { throw new AccessDeniedException(权限不足); } } } // 3. 使用注解 Service public class AdminService { RequiredPermission({user:delete, admin}) public void deleteUser(long userId) { // 删除用户逻辑 } }五、Spring AOP 实现原理5.1 JDK 动态代理 vs CGLIB特性JDK 动态代理CGLIB原理基于接口基于类继承速度创建慢执行快创建快执行稍慢限制只能代理实现了接口的类不能代理 final 类/方法默认选择目标类有接口时目标类无接口时5.2 解决 AOP 自调用问题问题同类内部调用不会触发 AOP 代理。Service public class UserService { public void outerMethod() { innerMethod(); // 自调用AOP 不生效 } public void innerMethod() { // ... } }解决方案Service public class UserService { Autowired private ApplicationContext context; public void outerMethod() { // 通过容器获取代理对象 UserService proxy context.getBean(UserService.class); proxy.innerMethod(); // AOP 生效 } }六、实战场景微服务统一限流与熔断保护在微服务架构中服务间的调用关系错综复杂当某个服务出现故障或响应缓慢时可能会导致级联故障雪崩效应。通过IOC 管理限流规则配置AOP 实现无侵入式限流熔断可以优雅地解决这个问题 。6.1 场景背景痛点传统方案AOP IOC 方案每个接口都要写限流代码代码冗余维护困难注解驱动零侵入限流规则硬编码修改需重启服务配置中心动态刷新熔断逻辑分散难以统一管理切面集中处理6.2 完整代码实现步骤 1定义限流注解Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RateLimit { // 每秒允许的请求数 double permitsPerSecond() default 10.0; // 等待超时时间毫秒 long timeout() default 0; // 限流提示消息 String message() default 系统繁忙请稍后再试; }步骤 2定义熔断注解Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface CircuitBreaker { // 失败率阈值百分比 int failureRateThreshold() default 50; // 慢调用比例阈值 int slowCallRateThreshold() default 50; // 熔断持续时间秒 int waitDurationInOpenState() default 30; // 最小调用次数 int minimumNumberOfCalls() default 10; }步骤 3限流熔断切面核心Aspect Component Order(1) // 确保在事务切面之前执行 public class ResilienceAspect { // IOC 注入限流器缓存Guava Cache 或 Caffeine private final LoadingCacheString, RateLimiter rateLimiterCache; // IOC 注入熔断器注册表 private final MapString, CircuitBreaker circuitBreakerRegistry; // IOC 注入配置中心客户端如 Nacos Autowired private DynamicConfigClient configClient; public ResilienceAspect() { // 初始化限流器缓存 this.rateLimiterCache Caffeine.newBuilder() .expireAfterAccess(1, TimeUnit.HOURS) .build(key - RateLimiter.create(10.0)); // 默认 10 QPS } /** * 限流处理 */ Around(annotation(rateLimit)) public Object handleRateLimit(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable { String methodKey pjp.getSignature().toShortString(); // 从配置中心获取动态限流阈值支持热更新 double dynamicRate configClient.getDouble(ratelimit. methodKey, rateLimit.permitsPerSecond()); RateLimiter limiter rateLimiterCache.get(methodKey, k - RateLimiter.create(dynamicRate)); // 尝试获取许可 boolean acquired limiter.tryAcquire(rateLimit.timeout(), TimeUnit.MILLISECONDS); if (!acquired) { // 触发限流返回友好提示 throw new RateLimitException(rateLimit.message()); } return pjp.proceed(); } /** * 熔断处理 */ Around(annotation(circuitBreaker)) public Object handleCircuitBreaker(ProceedingJoinPoint pjp, CircuitBreaker cbConfig) throws Throwable { String methodKey pjp.getSignature().toShortString(); CircuitBreaker breaker circuitBreakerRegistry.computeIfAbsent(methodKey, k - { return CircuitBreaker.ofDefaults(k, CircuitBreakerConfig.custom() .failureRateThreshold(cbConfig.failureRateThreshold()) .slowCallRateThreshold(cbConfig.slowCallRateThreshold()) .slowCallDurationThreshold(Duration.ofSeconds(2)) .permittedNumberOfCallsInHalfOpenState(5) .waitDurationInOpenState(Duration.ofSeconds(cbConfig.waitDurationInOpenState())) .minimumNumberOfCalls(cbConfig.minimumNumberOfCalls()) .build()); }); // 检查熔断器状态 if (breaker.getState() CircuitBreaker.State.OPEN) { // 熔断开启执行降级逻辑 return executeFallback(pjp); } long start System.currentTimeMillis(); try { Object result pjp.proceed(); // 记录成功 breaker.recordSuccess(); // 记录慢调用 long duration System.currentTimeMillis() - start; if (duration 2000) { breaker.recordSlowCall(); } return result; } catch (Exception e) { // 记录失败 breaker.recordFailure(); throw e; } } /** * 降级方法 */ private Object executeFallback(ProceedingJoinPoint pjp) { // 返回缓存数据、默认值或友好提示 return ApiResponse.fail(服务暂时不可用请稍后重试); } }步骤 4服务层使用零侵入业务代码Service public class OrderService { Autowired private OrderRepository orderRepository; Autowired private InventoryClient inventoryClient; // 远程服务调用 /** * 创建订单限流 熔断双重保护 * 当库存服务故障时自动熔断防止雪崩 */ RateLimit(permitsPerSecond 100, timeout 100) CircuitBreaker(failureRateThreshold 60, waitDurationInOpenState 30) Transactional public Order createOrder(CreateOrderRequest request) { // 1. 扣减库存远程调用可能被熔断 inventoryClient.deductStock(request.getSkuId(), request.getQuantity()); // 2. 创建订单 Order order new Order(); order.setUserId(request.getUserId()); order.setAmount(request.getAmount()); order.setStatus(OrderStatus.PENDING); return orderRepository.save(order); } /** * 查询订单仅限流 */ RateLimit(permitsPerSecond 500) public Order getOrder(Long orderId) { return orderRepository.findById(orderId) .orElseThrow(() - new OrderNotFoundException(orderId)); } }步骤 5配置类IOC 容器管理Configuration EnableAspectJAutoProxy public class ResilienceConfig { Bean public ResilienceAspect resilienceAspect() { return new ResilienceAspect(); } /** * 配置中心监听实现限流规则热更新 */ Bean public ConfigChangeListener rateLimitConfigListener(DynamicConfigClient client) { return new ConfigChangeListener() { Override public void onChange(String key, String value) { if (key.startsWith(ratelimit.)) { // 刷新对应方法的限流器 String methodKey key.replace(ratelimit., ); double newRate Double.parseDouble(value); // 更新限流器... } } }; } }6.3 架构价值维度收益解耦限流熔断逻辑与业务代码完全分离可维护性集中管理所有服务的容错策略动态性通过配置中心实时调整限流阈值无需重启可观测性切面中统一收集熔断指标对接监控系统6.4 扩展方向多维度限流支持按用户 ID、IP 地址、API 路径等维度限流自适应熔断基于历史数据动态调整熔断阈值集成 Sentinel将自定义切面替换为 Alibaba Sentinel获得更丰富的流量控制功能分布式限流结合 Redis 实现集群级别的统一限流七、总何时使用 IOC对象依赖关系复杂时需要灵活替换实现时需要进行单元测试时需要集中管理对象生命周期时何时使用 AOP有横跨多个模块的通用功能时不想让业务代码被非业务代码污染时需要统一处理日志、事务、权限等关注点时IOC 和 AOP 是 Spring 框架的两大核心合理使用这两种技术可以显著提高代码的模块化程度、可维护性和可扩展性