Spring MVC实践与高级架构特性全

Spring MVC核心架构与处理流程剖析

1. 架构组件深度解析

Spring MVC采用经典前端控制器模式,其核心架构由以下关键组件构成:

  1. DispatcherServlet(中央调度器)
  • 继承自HttpServlet,作为统一请求入口
  • 初始化时加载WebApplicationContext
  • 默认配置路径:/WEB-INF/ -servlet.xml
  • 多DispatcherServlet配置示例:
<servlet>
    <servlet-name>adminServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/admin-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
  1. HandlerMapping 策略
  • BeanNameUrlHandlerMapping:Bean名称映射
  • ControllerClassNameHandlerMapping:类名约定映射
  • RequestMappingHandlerMapping(推荐):注解驱动
  • 自定义映射策略实现:
public class VersionHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String version = request.getHeader("API-Version");
        // 根据版本选择处理器
        return super.getHandlerInternal(request);
    }
}
  1. HandlerAdapter 适配器体系
  • HttpRequestHandlerAdapter
  • SimpleControllerHandlerAdapter
  • RequestMappingHandlerAdapter
  • 自定义适配器示例:
public class GraphQLHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof GraphQLController;
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler) throws Exception {
        // 处理GraphQL请求逻辑
        return new ModelAndView();
    }
}

2. 请求处理全生命周期

典型请求处理流程:

  1. 请求到达阶段
  • 字符编码过滤(CharacterEncodingFilter)
  • 请求上下文路径处理
  • 多部分解析(MultipartResolver)
  1. 核心处理阶段
sequenceDiagram
    participant Client
    participant DispatcherServlet
    participant HandlerMapping
    participant HandlerAdapter
    participant Interceptor
    participant Controller
    
    Client->>DispatcherServlet: HTTP Request
    DispatcherServlet->>HandlerMapping: getHandler()
    HandlerMapping-->>DispatcherServlet: HandlerExecutionChain
    DispatcherServlet->>HandlerAdapter: handle()
    HandlerAdapter->>Interceptor: preHandle()
    Interceptor-->>HandlerAdapter: boolean
    HandlerAdapter->>Controller: execute()
    Controller-->>HandlerAdapter: ModelAndView
    HandlerAdapter->>Interceptor: postHandle()
    DispatcherServlet->>ViewResolver: resolveViewName()
    ViewResolver-->>DispatcherServlet: View
    DispatcherServlet->>View: render()
    View-->>Client: Response
  1. 异常处理流程
  • HandlerExceptionResolver 体系
  • @ExceptionHandler 方法优先级
  • 全局异常处理 vs 控制器局部异常处理

3. 控制器进阶设计模式

3.1 方法参数绑定机制

@PostMapping("/users")
public ResponseEntity<User> createUser(
    @Valid @RequestBody UserDTO userDTO,
    @RequestHeader("X-Client-Version") String clientVersion,
    UriComponentsBuilder uriBuilder) {
    
    User savedUser = userService.save(userDTO);
    URI location = uriBuilder.path("/users/{id}")
                           .buildAndExpand(savedUser.getId())
                           .toUri();
    return ResponseEntity.created(location).body(savedUser);
}

3.2 参数验证进阶

自定义验证注解示例:

@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
    String message() default "Invalid phone number";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
    private static final Pattern PHONE_PATTERN = 
        Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && PHONE_PATTERN.matcher(value).matches();
    }
}

3.3 响应处理策略

内容协商配置示例:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
            .favorParameter(true)
            .parameterName("format")
            .ignoreAcceptHeader(false)
            .defaultContentType(MediaType.APPLICATION_JSON)
            .mediaType("json", MediaType.APPLICATION_JSON)
            .mediaType("xml", MediaType.APPLICATION_XML);
    }
}

4. 视图技术深度集成

4.1 Thymeleaf高级集成

模板布局示例:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Default Title</title>
</head>
<body>
    <div layout:fragment="header">
        <!-- 公共头部 -->
    </div>
    
    <div layout:fragment="content">
        <!-- 内容占位 -->
    </div>
</body>
</html>

4.2 PDF导出实现

使用Flying Saucer生成PDF:

@GetMapping(value = "/report.pdf", produces = "application/pdf")
public ResponseEntity<byte[]> generatePdfReport() throws Exception {
    Context context = new Context();
    context.setVariable("data", reportService.getData());
    
    String html = templateEngine.process("report-template", context);
    
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ITextRenderer renderer = new ITextRenderer();
    renderer.setDocumentFromString(html);
    renderer.layout();
    renderer.createPDF(outputStream);
    
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_PDF);
    headers.setContentDisposition(
        ContentDisposition.attachment().filename("report.pdf").build());
    
    return new ResponseEntity<>(outputStream.toByteArray(), headers, HttpStatus.OK);
}

5. 异步处理与响应式支持

5.1 DeferredResult应用

长轮询示例:

@GetMapping("/async/events")
public DeferredResult<ResponseEntity<List<Event>>> getEvents() {
    DeferredResult<ResponseEntity<List<Event>>> deferredResult = 
        new DeferredResult<>(30_000L);
    
    eventQueue.register(deferredResult);
    
    deferredResult.onTimeout(() -> 
        deferredResult.setErrorResult(
            ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).build()));
    
    deferredResult.onCompletion(() -> 
        eventQueue.unregister(deferredResult));
    
    return deferredResult;
}

5.2 WebFlux整合

混合使用MVC和WebFlux:

@RestController
@RequiredArgsConstructor
public class HybridController {
    private final WebClient webClient;
    
    @GetMapping("/hybrid")
    public Mono<String> hybridEndpoint() {
        return webClient.get()
            .uri("http://api.example.com/data")
            .retrieve()
            .bodyToMono(String.class)
            .flatMap(data -> Mono.fromCallable(() -> processData(data)));
    }
    
    @Async
    private String processData(String data) {
        // 阻塞操作放入单独线程池
        return expensiveProcessing(data);
    }
}

6. 安全增强实践

6.1 CSRF防护配置

自定义CSRF令牌仓库:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .csrfTokenRepository(new CookieCsrfTokenRepository())
                .requireCsrfProtectionMatcher(
                    new RequestMatcher() {
                        private final Pattern allowedMethods = 
                            Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
                        
                        @Override
                        public boolean matches(HttpServletRequest request) {
                            return !allowedMethods.matcher(request.getMethod()).matches();
                        }
                    });
    }
}

6.2 XSS防御策略

自定义HTML转义器:

public class CustomHtmlEscaper extends HtmlUtils {
    
    public static String escape(String input) {
        if (input == null) return null;
        return StringEscapeUtils.escapeHtml4(input)
            .replace("'", "&#39;")
            .replace("`", "&#96;");
    }
}

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldAnnotation(new StringFormatFormatter());
    }
    
    private static class StringFormatFormatter implements AnnotationFormatterFactory<XssSafe> {
        
        @Override
        public Set<Class<?>> getFieldTypes() {
            return Collections.singleton(String.class);
        }
        
        @Override
        public Printer<?> getPrinter(XssSafe annotation, Class<?> fieldType) {
            return (Printer<String>) CustomHtmlEscaper::escape;
        }
        
        @Override
        public Parser<?> getParser(XssSafe annotation, Class<?> fieldType) {
            return (Parser<String>) text -> CustomHtmlEscaper.escape(text);
        }
    }
}

7. 性能优化策略

7.1 缓存配置示例

方法级缓存:

@GetMapping("/products/{id}")
@Cacheable(value = "products", key = "#id", 
           unless = "#result == null || #result.price < 100")
public Product getProduct(@PathVariable Long id) {
    return productRepository.findById(id).orElseThrow();
}

7.2 连接池优化

HikariCP配置:

spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.leak-detection-threshold=5000

8. 微服务环境下的Spring MVC

8.1 分布式Session管理

Spring Session Redis配置:

@EnableRedisHttpSession
@Configuration
public class SessionConfig {
    
    @Bean
    public LettuceConnectionFactory connectionFactory() {
        return new LettuceConnectionFactory(
            new RedisStandaloneConfiguration("redis-server", 6379));
    }
    
    @Bean
    public HttpSessionIdResolver sessionIdResolver() {
        return HeaderHttpSessionIdResolver.xAuthToken();
    }
}

8.2 API版本控制策略

Content Negotiation版本控制:

@GetMapping(value = "/api/products", 
           produces = "application/vnd.company.v1+json")
public List<Product> getProductsV1() {
    return productService.getAllProducts();
}

@GetMapping(value = "/api/products", 
           produces = "application/vnd.company.v2+json")
public List<ProductDTO> getProductsV2() {
    return productService.getAllProductDTOs();
}

9. 测试驱动开发实践

9.1 MockMvc高级用法

REST API测试示例:

@SpringBootTest
@AutoConfigureMockMvc
class ProductControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void shouldReturnProduct() throws Exception {
        mockMvc.perform(get("/api/products/123")
               .header("X-API-Version", "2")
               .accept(MediaType.APPLICATION_JSON))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.id").value(123))
               .andExpect(jsonPath("$.price").isNumber())
               .andDo(document("get-product",
                    pathParameters(
                        parameterWithName("id").description("Product ID")
                    ),
                    responseFields(
                        fieldWithPath("id").description("产品ID"),
                        fieldWithPath("name").description("产品名称"),
                        fieldWithPath("price").description("产品价格")
                    )));
    }
}

9.2 集成测试策略

Testcontainers集成示例:

@SpringBootTest
@Testcontainers
class IntegrationTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13");
    
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }
    
    @Test
    void contextLoads() {
        // 测试数据库交互
    }
}
正文到此结束
评论插件初始化中...
Loading...