Spring Boot统一功能处理深度解析

第一章:为什么需要统一功能处理?

想象你正在开发一个电商系统,包含用户管理、商品管理、订单管理等模块。每个模块都需要:

  1. 用户身份验证
  2. 操作日志记录
  3. 异常统一处理
  4. 数据格式标准化

如果每个模块都单独实现这些功能:

java 复制代码
// 用户模块
@PostMapping("/users")
public User createUser(@RequestBody User user) {
    try {
        // 1. 验证token
        // 2. 记录日志
        // 3. 业务处理
        // 4. 统一返回格式
    } catch (Exception e) {
        // 5. 异常处理
    }
}

// 商品模块
@PostMapping("/products")
public Product createProduct(@RequestBody Product product) {
    try {
        // 重复步骤1-5...
    } catch (Exception e) {
        // ...
    }
}

问题暴露

  • 代码重复率高(DRY原则被破坏)
  • 维护困难(修改需多处调整)
  • 系统一致性差(不同开发者实现方式不同)

第二章:Spring Boot统一处理四大核心组件

2.1 拦截器(Interceptor) - 请求的"安检门"

作用:在请求到达Controller前/后执行通用逻辑

java 复制代码
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) {
        // 1. 从请求头获取Token(就像门卫检查你的门禁卡)
        String token = request.getHeader("Authorization");
        
        // 2. 验证Token是否有效(门卫刷卡机验证)
        if (!validateToken(token)) {
            // 3. 无效则拒绝进入(设置401状态码)
            response.setStatus(401);
            return false; // 中断请求
        }
        return true; // 允许进入
    }
    
    // 验证Token的方法(门卫的验证设备)
    private boolean validateToken(String token) {
        // 实际验证逻辑(这里简化为非空检查)
        return token != null && !token.isEmpty();
    }
}

通俗解释

  • 这个类就像小区的门卫,检查每个进入的人(请求)
  • preHandle方法:在业主回家前检查门禁卡(Token)
  • 如果门禁卡无效(validateToken返回false),门卫会拒绝进入(返回401状态码)
  • 如果有效,就放行(return true)

配置方式

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 给门卫分配工作区域
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/api/**")   // 保护所有/api路径
                .excludePathPatterns("/api/login"); // 但登录入口不需要检查
    }
}

通俗解释

  • 这相当于给门卫划定工作范围:
    • /api/**:需要检查的所有区域
    • /api/login:特殊通道(登录入口),不需要检查
2.2 过滤器(Filter) - 请求的"净化器"

作用:处理HTTP请求/响应的原始数据

java 复制代码
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        // 1. 记录请求开始时间(水电表开始计数)
        long startTime = System.currentTimeMillis();
        
        // 2. 继续处理请求(让水流/电流继续)
        chain.doFilter(request, response);
        
        // 3. 计算耗时(计算水电用量)
        long duration = System.currentTimeMillis() - startTime;
        
        // 4. 记录日志(生成水电账单)
        System.out.println("请求耗时: " + duration + "ms");
    }
}
  • 这个过滤器就像水电表:
    1. 请求开始时记录时间(开水龙头)
    2. chain.doFilter让请求继续处理(水流)
    3. 请求结束后计算耗时(关水龙头,计算用水量)
    4. 记录日志(生成水电账单)

与拦截器区别

特性 过滤器(Filter) 拦截器(Interceptor)
作用范围 Servlet规范 Spring MVC特有
依赖 不依赖Spring容器 依赖Spring容器
获取信息 原始HTTP请求/响应 HandlerMethod信息
执行顺序 最先执行 在DispatcherServlet后执行
2.3 切面(AOP) - 功能的"手术刀"

作用:在方法执行前后插入通用逻辑

java 复制代码
@Aspect
@Component
public class ServiceMonitorAspect {
    
    // 监控Service层方法
    @Around("execution(* com.example..service.*.*(..))")
    public Object monitorService(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed(); // 执行目标方法
        } finally {
            long duration = System.currentTimeMillis() - start;
            // 记录慢查询
            if (duration > 500) {
                log.warn("慢方法: {} 耗时: {}ms", 
                         pjp.getSignature(), duration);
            }
        }
    }
}
2.4 控制器增强(@ControllerAdvice) - 全局的"管理员"

作用:统一处理控制器异常和返回格式

java 复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    
    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResponseResult<Void> handleBusinessEx(BusinessException e) {
        return ResponseResult.error(e.getCode(), e.getMessage());
    }
    
    // 处理所有未捕获异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseResult<Void> handleException(Exception e) {
        log.error("系统异常", e);
        return ResponseResult.error(500, "系统繁忙");
    }
}

第三章:统一功能处理实战案例

3.1 统一响应格式
java 复制代码
// 标准响应体
public class ResponseResult<T> {
    private int code;
    private String msg;
    private T data;
    
    // 成功响应
    public static <T> ResponseResult<T> success(T data) {
        return new ResponseResult<>(200, "成功", data);
    }
    
    // 错误响应
    public static <T> ResponseResult<T> error(int code, String msg) {
        return new ResponseResult<>(code, msg, null);
    }
}

// 控制器统一返回
@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public ResponseResult<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return ResponseResult.success(user);
    }
}
3.2 统一异常处理
java 复制代码
@ControllerAdvice // 1. 声明这是全局异常处理器
public class GlobalExceptionHandler {
    
    // 2. 处理业务异常(如订单处理失败)
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResponseResult<Void> handleBusinessEx(BusinessException e) {
        // 返回标准错误格式
        return ResponseResult.error(e.getCode(), e.getMessage());
    }
    
    // 3. 处理所有未预料异常(系统崩溃)
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseResult<Void> handleException(Exception e) {
        // 记录错误日志(就像应急系统记录事故)
        e.printStackTrace();
        // 返回友好提示
        return ResponseResult.error(500, "系统繁忙,请稍后再试");
    }
}
  • @ControllerAdvice:这是整个小区的应急中心
  • @ExceptionHandler:不同类型的应急处理小组
    • BusinessException:处理业务问题(如订单错误)
    • Exception:处理所有未预料的问题(系统崩溃)
  • 当发生问题时,系统会自动调用对应的方法处理
3.3 统一日志记录
java 复制代码
@Aspect
@Component
public class ControllerLogAspect {
    
    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object logController(ProceedingJoinPoint pjp) throws Throwable {
        // 获取请求信息
        HttpServletRequest request = 
            ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
            .getRequest();
        
        // 记录请求日志
        log.info("请求开始 => URI: {}, 参数: {}", 
                 request.getRequestURI(), 
                 Arrays.toString(pjp.getArgs()));
        
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();
            // 记录响应日志
            log.info("请求完成 <= URI: {}, 耗时: {}ms", 
                     request.getRequestURI(), 
                     System.currentTimeMillis() - start);
            return result;
        } catch (Exception e) {
            // 记录异常日志
            log.error("请求异常 <= URI: {}, 错误: {}", 
                      request.getRequestURI(), e.getMessage());
            throw e;
        }
    }
}
3.4 统一身份认证
java 复制代码
public class JwtAuthInterceptor implements HandlerInterceptor {
    
    @Autowired
    private JwtTokenService tokenService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) {
        // 1. 获取Token
        String token = request.getHeader("Authorization");
        if (StringUtils.isEmpty(token)) {
            sendError(response, 401, "未提供认证信息");
            return false;
        }
        
        // 2. 验证Token
        Claims claims = tokenService.parseToken(token);
        if (claims == null) {
            sendError(response, 401, "无效的Token");
            return false;
        }
        
        // 3. 存储用户信息
        UserContext.setCurrentUser(claims.getSubject());
        return true;
    }
    
    private void sendError(HttpServletResponse response, int status, String msg) {
        response.setStatus(status);
        response.setContentType("application/json");
        try {
            response.getWriter().write(
                new ResponseResult<>(status, msg).toString()
            );
        } catch (IOException e) {
            log.error("响应写入失败", e);
        }
    }
}

第四章:高级应用场景

4.1 接口限流(Rate Limiting)
java 复制代码
@Aspect
@Component
public class RateLimitAspect {
    
    private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
    
    @Around("@annotation(rateLimit)")
    public Object rateLimit(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable {
        String key = getRateLimitKey(pjp);
        RateLimiter limiter = limiters.computeIfAbsent(key, 
            k -> RateLimiter.create(rateLimit.value()));
        
        if (limiter.tryAcquire()) {
            return pjp.proceed();
        } else {
            throw new BusinessException(429, "请求过于频繁");
        }
    }
    
    private String getRateLimitKey(ProceedingJoinPoint pjp) {
        // 根据方法+IP生成唯一key
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        HttpServletRequest request = 
            ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
            .getRequest();
        return signature.getMethod().getName() + ":" + request.getRemoteAddr();
    }
}

// 使用注解
@GetMapping("/api/data")
@RateLimit(value = 10) // 每秒10次
public ResponseResult<Data> getData() {
    // ...
}
4.2 多租户数据隔离
java 复制代码
public class TenantInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) {
        // 从请求头获取租户ID
        String tenantId = request.getHeader("X-Tenant-ID");
        if (StringUtils.isNotBlank(tenantId)) {
            TenantContext.setCurrentTenant(tenantId);
        }
        return true;
    }
}

// 在MyBatis拦截器中应用
@Intercepts({@Signature(type = Executor.class, method = "query", ...)})
public class TenantInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 自动添加租户过滤条件
        String tenantId = TenantContext.getCurrentTenant();
        if (tenantId != null) {
            MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
            Object parameter = invocation.getArgs()[1];
            // 修改SQL添加 tenant_id = #{tenantId}
        }
        return invocation.proceed();
    }
}
4.3 API版本控制
java 复制代码
public class ApiVersionInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) {
        // 从URL路径获取版本号
        String path = request.getRequestURI();
        Matcher matcher = Pattern.compile("/v(\\d+)/").matcher(path);
        if (matcher.find()) {
            int version = Integer.parseInt(matcher.group(1));
            ApiVersionContext.setVersion(version);
        }
        return true;
    }
}

// 控制器根据版本路由
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping
    @ApiVersion(1) // v1版本
    public ResponseResult<List<User>> getUsersV1() {
        // 旧版逻辑
    }
    
    @GetMapping
    @ApiVersion(2) // v2版本
    public ResponseResult<List<UserDto>> getUsersV2() {
        // 新版逻辑
    }
}

第五章:性能优化与最佳实践

5.1 组件执行顺序优化
复制代码
请求进入

Filter-Interceptor-preHandle-Controller-Interceptor-postHandle-Filter-after-Response

优化策略

  1. 过滤器优先处理原始数据(如请求体缓存)
  2. 拦截器处理业务相关逻辑(如认证、日志)
  3. 切面处理具体方法逻辑(如性能监控)
  4. @ControllerAdvice最后处理异常和响应
5.2 避免的陷阱
  1. 循环依赖问题

java 复制代码
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Autowired
    private AuthService authService; // 可能导致循环依赖
}

解决方案 :使用@Lazy延迟注入
3.

java 复制代码
@Lazy
@Autowired
private AuthService authService;

线程安全问题
4.

java 复制代码
public class UserContext {
    private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
}

性能热点
5.

java 复制代码
// 避免在拦截器/过滤器中执行耗时操作
public boolean preHandle(...) {
    // 错误:执行数据库查询
    User user = userRepository.findById(userId);
}
5.3 最佳实践总结
  1. 职责分离原则

    • 过滤器:处理原始HTTP请求/响应
    • 拦截器:处理业务相关前置/后置逻辑
    • 切面:处理具体方法逻辑
    • 控制器增强:统一异常和响应处理
  2. 配置顺序原则

java 复制代码
@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 1. 认证拦截器(最先执行)
    registry.addInterceptor(authInterceptor);
    
    // 2. 日志拦截器
    registry.addInterceptor(logInterceptor);
    
    // 3. 其他业务拦截器
}

性能监控指标
9.

java 复制代码
// 使用Micrometer监控
@Autowired
private MeterRegistry meterRegistry;

public void afterCompletion(...) {
    meterRegistry.counter("api.requests", 
        "uri", request.getRequestURI(),
        "status", String.valueOf(response.getStatus())
    ).increment();
}

第六章:综合实战 - 电商系统统一处理

6.1 系统架构图

6.2 核心配置代码

java 复制代码
// 网关统一配置
@Configuration
public class GatewayConfig {
    
    @Bean
    public GlobalFilter customFilter() {
        return (exchange, chain) -> {
            // 1. 统一添加请求ID
            ServerHttpRequest request = exchange.getRequest().mutate()
                .header("X-Request-ID", UUID.randomUUID().toString())
                .build();
            return chain.filter(exchange.mutate().request(request).build());
        };
    }
}

// 微服务统一配置
@Configuration
public class ServiceConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TenantInterceptor());
        registry.addInterceptor(new AuthInterceptor());
    }
    
    @Bean
    public FilterRegistrationBean<LoggingFilter> loggingFilter() {
        FilterRegistrationBean<LoggingFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new LoggingFilter());
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}
6.3 业务服务示例
java 复制代码
@RestController
@RequestMapping("/orders")
public class OrderController {
    
    @PostMapping
    public ResponseResult<Order> createOrder(@Valid @RequestBody OrderCreateDTO dto) {
        // 无需处理认证、日志、异常等
        Order order = orderService.createOrder(dto);
        return
相关推荐
Kali_075 分钟前
使用 Mathematical_Expression 从零开始实现数学题目的作答小游戏【可复制代码】
java·人工智能·免费
rzl0216 分钟前
java web5(黑马)
java·开发语言·前端
君爱学习22 分钟前
RocketMQ延迟消息是如何实现的?
后端
guojl36 分钟前
深度解读jdk8 HashMap设计与源码
java
Falling4239 分钟前
使用 CNB 构建并部署maven项目
后端
guojl41 分钟前
深度解读jdk8 ConcurrentHashMap设计与源码
java
程序员小假1 小时前
我们来讲一讲 ConcurrentHashMap
后端
爱上语文1 小时前
Redis基础(5):Redis的Java客户端
java·开发语言·数据库·redis·后端
A~taoker1 小时前
taoker的项目维护(ng服务器)
java·开发语言