Spring MVC作为Java EE领域最主流的Web框架之一,其优雅的设计思想和模块化架构一直是开发者学习的典范。今天我们就来解剖这个经典框架的"五脏六腑",看看支撑起整个Spring MVC运转的九大核心组件究竟如何协同工作。
在实际项目开发中,很多开发者虽然能熟练使用Spring MVC完成功能开发,但对框架内部的核心组件却知之甚少。这就像会开车但不了解汽车发动机原理一样,遇到复杂问题时往往无从下手。通过深入理解这些核心组件,开发者可以:
Spring MVC的九大核心组件可以分为三个层次:
code复制┌───────────────────────────────────────┐
│ 前端控制器层 │
│ (DispatcherServlet) │
└───────────────────────────────────────┘
↓
┌───────────────────────────────────────┐
│ 策略接口层 │
│ (HandlerMapping/HandlerAdapter等) │
└───────────────────────────────────────┘
↓
┌───────────────────────────────────────┐
│ 基础服务层 │
│ (ViewResolver/HandlerException等) │
└───────────────────────────────────────┘
典型请求处理流程中各组件的协作时序:
作为整个框架的"大脑",DispatcherServlet承担着请求分发的核心职责。它的工作流程可以概括为:
java复制protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 1. 确定Handler
HandlerExecutionChain mappedHandler = getHandler(request);
// 2. 获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 执行拦截器preHandle
if (!mappedHandler.applyPreHandle(request, response)) return;
// 4. 实际处理请求
ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
// 5. 应用默认视图名
applyDefaultViewName(request, mv);
// 6. 执行拦截器postHandle
mappedHandler.applyPostHandle(request, response, mv);
// 7. 处理结果
processDispatchResult(request, response, mappedHandler, mv, dispatchException);
}
关键设计点:DispatcherServlet采用"前端控制器"模式,集中处理所有请求,但将具体业务处理委托给其他组件,实现了"单一职责"和"开放封闭"原则。
HandlerMapping的核心职责是将URL请求映射到具体的处理器(Handler)。Spring MVC提供了多种实现:
| 实现类 | 映射策略 | 适用场景 |
|---|---|---|
| BeanNameUrlHandlerMapping | 根据Bean名称映射 | 简单应用 |
| RequestMappingHandlerMapping | 注解驱动映射 | 现代应用 |
| SimpleUrlHandlerMapping | 显式URL配置映射 | 需要精确控制 |
配置示例:
xml复制<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/welcome=homeController
/user/*=userController
</value>
</property>
</bean>
HandlerAdapter定义了统一的请求处理接口,使得不同类型的处理器能够以统一的方式工作。常见实现包括:
适配器模式的价值:
java复制public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
这种设计让框架可以支持各种类型的处理器,而不需要修改核心分发逻辑。
ViewResolver负责将逻辑视图名解析为具体的View实现。常用的视图解析策略包括:
InternalResourceViewResolver:解析JSP等内部资源
properties复制prefix=/WEB-INF/views/
suffix=.jsp
ContentNegotiatingViewResolver:基于内容协商
xml复制<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</list>
</property>
</bean>
XmlViewResolver:XML配置视图映射
xml复制<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
异常解析器允许开发者以声明方式处理异常,常见的实现策略:
自定义异常解析器示例:
java复制public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
if(ex instanceof BusinessException) {
return new ModelAndView("error/business", "error", ex.getMessage());
}
return null; // 继续其他解析器
}
}
通过DispatcherServlet的contextConfigLocation参数可以优化组件加载:
xml复制<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/mvc-core.xml
/WEB-INF/spring/mvc-views.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Spring MVC 3.2+引入了异步处理支持,关键配置:
java复制@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(30000);
configurer.registerCallableInterceptors(new TimeoutCallableProcessingInterceptor());
}
}
| 参数 | 建议值 | 说明 |
|---|---|---|
| spring.mvc.async.request-timeout | 30000 | 异步请求超时(ms) |
| spring.mvc.servlet.load-on-startup | 1 | 启动优先级 |
| spring.mvc.static-path-pattern | /** | 静态资源匹配模式 |
Spring MVC各组件的初始化顺序有时会导致意外行为。例如,自定义的HandlerMapping需要在RequestMappingHandlerMapping之前加载时,应该:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
@Order(0) // 确保优先加载
public HandlerMapping customHandlerMapping() {
return new CustomHandlerMapping();
}
}
多个ViewResolver共存时的常见问题及解决方案:
java复制@Configuration
public class ViewResolverConfig {
@Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setOrder(1); // 优先尝试Thymeleaf
return resolver;
}
@Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setOrder(2); // 其次尝试JSP
return resolver;
}
}
| 特性 | HandlerInterceptor | Filter |
|---|---|---|
| 所处层次 | Spring MVC层面 | Servlet容器层面 |
| 依赖 | 可访问Spring上下文 | 无法直接使用Spring Bean |
| 执行时机 | Controller方法前后 | Servlet处理前后 |
| 配置方式 | WebMvcConfigurer | web.xml或@WebFilter |
实现一个基于数据库配置的URL映射:
java复制public class DatabaseHandlerMapping extends AbstractHandlerMapping {
private MappingRepository mappingRepository;
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String url = request.getRequestURI();
return mappingRepository.findHandlerByUrl(url);
}
// 省略setter方法
}
实现一个支持多租户的视图解析器:
java复制public class TenantAwareViewResolver implements ViewResolver {
private Map<String, ViewResolver> resolvers = new ConcurrentHashMap<>();
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
String tenant = TenantContext.getCurrentTenant();
ViewResolver resolver = resolvers.computeIfAbsent(tenant,
k -> createTenantResolver(k));
return resolver.resolveViewName(viewName, locale);
}
private ViewResolver createTenantResolver(String tenant) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/tenants/" + tenant + "/");
resolver.setSuffix(".jsp");
return resolver;
}
}
实现一个RESTful风格的异常处理器:
java复制public class RestExceptionResolver implements HandlerExceptionResolver {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
try {
ErrorResponse error = new ErrorResponse(
ex.getClass().getSimpleName(),
ex.getMessage());
response.getWriter().write(objectMapper.writeValueAsString(error));
return new ModelAndView();
} catch (IOException e) {
return null;
}
}
@Data
@AllArgsConstructor
private static class ErrorResponse {
private String error;
private String message;
}
}
Spring Boot对Spring MVC组件提供了自动配置:
java复制@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ DispatcherServlet.class, WebMvcConfigurer.class })
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebMvcAutoConfiguration {
// 自动配置各种默认组件
}
可以通过以下方式覆盖默认配置:
java复制@Configuration
public class CustomWebMvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
}
Spring Security通过Filter介入请求处理流程,与Spring MVC组件的交互点:
Spring WebFlux与Spring MVC组件的对应关系:
| MVC组件 | WebFlux对应组件 |
|---|---|
| DispatcherServlet | DispatcherHandler |
| HandlerMapping | RequestMappingInfoHandlerMapping |
| HandlerAdapter | HandlerMethodAdapter |
| ViewResolver | ViewResolver (兼容) |
关键监控指标及采集方式:
DispatcherServlet处理时间
java复制@RestController
@RequestMapping("/monitor")
public class MonitorController {
@Autowired
private DispatcherServlet dispatcherServlet;
@GetMapping("/metrics")
public Map<String, Object> getMetrics() {
Map<String, Object> metrics = new HashMap<>();
metrics.put("requestCount", dispatcherServlet.getRequestCount());
return metrics;
}
}
HandlerMapping匹配统计
java复制public class InstrumentedHandlerMapping extends RequestMappingHandlerMapping {
private AtomicLong matchCounter = new AtomicLong();
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
HandlerMethod handler = super.getHandlerInternal(request);
if(handler != null) {
matchCounter.incrementAndGet();
}
return handler;
}
public long getMatchCount() {
return matchCounter.get();
}
}
Spring Boot Actuator:提供端点监控
properties复制management.endpoints.web.exposure.include=metrics,mappings
自定义HandlerInterceptor:记录请求处理时间
java复制public class TimingInterceptor implements HandlerInterceptor {
private ThreadLocal<Long> startTime = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
startTime.set(System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
long duration = System.currentTimeMillis() - startTime.get();
log.info("Request {} took {} ms", request.getRequestURI(), duration);
}
}
| 特性 | Spring MVC | Spring WebFlux | JAX-RS |
|---|---|---|---|
| 编程模型 | 命令式 | 响应式 | 命令式 |
| 并发模型 | 线程池 | 事件循环 | 线程池 |
| 适用场景 | 传统应用 | 高并发IO | REST服务 |
| 学习曲线 | 平缓 | 较陡 | 中等 |
在实际项目中选择时,应该考虑团队技术栈、性能需求和应用场景等因素,而不是盲目追求新技术。