前后端分离项目中Spring MVC的请求执行流程

发布于:2025-08-13 ⋅ 阅读:(13) ⋅ 点赞:(0)

在前后端分离架构中,Spring MVC的执行流程与传统服务端渲染模式有显著差异,主要变化在于视图解析响应处理环节。以下是完整的执行流程分析,特别针对RESTful API场景:

一、核心执行流程(RESTful API场景)


二、关键阶段详细解析

1. 请求接收与分发阶段

DispatcherServlet的核心工作

  1. 初始化九大核心组件(包括HandlerMappingHandlerAdapter等)
  2. 处理Content-TypeAccept头部
  3. 处理CORS预检请求(OPTIONS方法)
  4. 根据请求路径选择对应的HandlerMapping

特殊处理

  • 对于application/json内容类型,会启用MappingJackson2HttpMessageConverter
  • 如果请求包含Authorization头,会触发安全过滤器链

2. 处理器映射阶段

HandlerMapping的工作机制

  1. RequestMappingHandlerMapping扫描所有@RestController注解的类
  2. 构建包含以下元素的映射注册表:
    • URL路径模式(支持Ant风格和路径变量)
    • 支持的HTTP方法(GET/POST/PUT等)
    • 消费/生产的媒体类型(consumes/produces)
    • 参数条件(@RequestParam等)
  1. 匹配优先级:
    • 精确路径优先于模式匹配
    • 最长路径模式优先

示例匹配过程

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    @GetMapping(value = "/{id}", 
               produces = MediaType.APPLICATION_JSON_VALUE)
    public UserDTO getUser(@PathVariable Long id) {
        // ...
    }
}

当请求GET /api/v1/users/123时:

  1. 匹配到UserController.getUser()方法
  2. 提取路径变量id=123
  3. 确认produces类型匹配

3. 参数绑定阶段

参数解析器链(HandlerMethodArgumentResolver)

参数类型

对应解析器

典型注解

URL路径参数

PathVariableMethodArgumentResolver

@PathVariable

查询参数

RequestParamMethodArgumentResolver

@RequestParam

JSON/XML请求体

RequestResponseBodyMethodProcessor

@RequestBody

请求头

RequestHeaderMethodArgumentResolver

@RequestHeader

Cookie值

ServletCookieValueMethodArgumentResolver

@CookieValue

Session属性

SessionAttributeMethodArgumentResolver

@SessionAttribute

复杂参数绑定示例

@PostMapping("/search")
public PageResult<UserDTO> searchUsers(
        @RequestBody UserQuery query,
        @RequestHeader("X-Auth-Token") String token,
        @Valid PageRequest pageRequest) {
    // ...
}

4. 业务处理阶段

控制器的典型职责

  1. 参数验证(结合JSR-303)
  2. 业务逻辑编排
  3. 异常捕获与转换
  4. 返回领域对象或DTO

RESTful最佳实践

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<UserDTO> createUser(
        @Valid @RequestBody UserCreateDTO dto) {
    
    UserDTO created = userService.createUser(dto);
    URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(created.getId())
            .toUri();
    
    return ResponseEntity.created(location).body(created);
}

5. 返回值处理阶段

ResponseBody处理机制

  1. RequestResponseBodyMethodProcessor处理返回值
  2. 选择匹配的HttpMessageConverter
    • MappingJackson2HttpMessageConverter:处理JSON
    • MappingJackson2XmlHttpMessageConverter:处理XML
    • ByteArrayHttpMessageConverter:处理字节数组
  1. 根据Accept头决定响应格式

转换器选择算法

  1. 检查控制器方法声明的produces类型
  2. 检查请求的Accept头
  3. 按照转换器注册顺序选择第一个能处理的

三、前后端分离特有处理

1. 统一响应封装模式

响应体封装策略

public class ApiResult<T> {
    private long timestamp = System.currentTimeMillis();
    private String code;
    private String message;
    private T data;
    
    public static <T> ApiResult<T> success(T data) {
        return new ApiResult<>("200", "success", data);
    }
    
    // 工厂方法省略...
}

通过ControllerAdvice实现统一包装

@RestControllerAdvice
public class ResponseWrapper implements ResponseBodyAdvice<Object> {
    
    @Override
    public boolean supports(MethodParameter returnType, 
                          Class<? extends HttpMessageConverter<?>> converterType) {
        // 不包装已经封装的结果或特定类型
        return !returnType.getParameterType().equals(ApiResult.class) 
            && !returnType.hasMethodAnnotation(NoWrapper.class);
    }
    
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                MediaType selectedContentType,
                                Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                ServerHttpRequest request, ServerHttpResponse response) {
        return ApiResult.success(body);
    }
}

2. 全局异常处理机制

异常处理中心

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ApiResult<Void>> handleBusinessException(BusinessException ex) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.error(ex.getErrorCode(), ex.getMessage()));
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResult<Map<String, String>> handleValidationException(
            MethodArgumentNotValidException ex) {
        
        Map<String, String> errors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .collect(Collectors.toMap(
                FieldError::getField,
                fieldError -> Optional.ofNullable(fieldError.getDefaultMessage())
                                     .orElse("")
            ));
        
        return ApiResult.error("VALIDATION_FAILED", "参数校验失败", errors);
    }
}

3. 跨域解决方案

精细化CORS配置

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("https://frontend.com")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("Content-Type", "Authorization")
                .exposedHeaders("X-Custom-Header")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

四、性能优化关键点

1. 消息转换器优化

Jackson自定义配置

@Configuration
public class JacksonConfig {
    
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
        return builder -> {
            builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
            builder.timeZone(TimeZone.getTimeZone("Asia/Shanghai"));
            builder.modules(new JavaTimeModule());
            builder.featuresToDisable(
                SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
                DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
            );
            builder.featuresToEnable(
                DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL,
                MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS
            );
        };
    }
}

2. 异步处理模式

异步控制器实现

@RestController
@RequestMapping("/async")
public class AsyncController {
    
    @GetMapping("/data")
    public CompletableFuture<ApiResult<Data>> fetchData() {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟耗时操作
            try { Thread.sleep(1000); } 
            catch (InterruptedException e) { /*...*/ }
            return dataService.getComplexData();
        }).thenApply(ApiResult::success);
    }
    
    @GetMapping("/deferred")
    public DeferredResult<ApiResult<Data>> deferredResult() {
        DeferredResult<ApiResult<Data>> result = new DeferredResult<>(5000L);
        CompletableFuture.runAsync(() -> {
            try {
                Data data = dataService.getComplexData();
                result.setResult(ApiResult.success(data));
            } catch (Exception e) {
                result.setErrorResult(e);
            }
        });
        return result;
    }
}

五、安全防护措施

1. 输入验证体系

DTO验证示例

public class UserCreateDTO {
    
    @NotBlank(message = "用户名不能为空")
    @Size(min = 4, max = 20, message = "用户名长度4-20个字符")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$",
             message = "密码必须至少8个字符,包含字母和数字")
    private String password;
    
    @AssertTrue(message = "必须接受用户协议")
    private boolean acceptedTerms;
}

2. 防XSS/CSRF策略

XSS过滤配置

@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
    FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(new XssFilter());
    registration.addUrlPatterns("/api/*");
    registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return registration;
}

public class XssFilter extends OncePerRequestFilter {
    private final XssCleaner xssCleaner = new DefaultXssCleaner();
    
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain chain) throws IOException, ServletException {
        chain.doFilter(new XssRequestWrapper(request, xssCleaner), response);
    }
}

通过以上深度解析,开发者可以全面掌握前后端分离架构下Spring MVC的完整工作流程,包括从请求接收到响应返回的每个关键环节,以及针对RESTful API场景的特殊处理方式和优化策略。


网站公告

今日签到

点亮在社区的每一天
去签到