面试官老王翘着二郎腿,悠然地品着茶,突然抬起头:
老王:“小李啊,听说你对Spring MVC很熟悉?那我问你个简单的问题:当用户在浏览器输入一个URL,比如 http://localhost:8080/user/123
,这个请求到达我们的Spring应用后,是怎么一步步处理的?”
小李(胸有成竹):“这个我知道!首先DispatcherServlet接收请求,然后找到对应的Controller…”
老王(打断):“慢着慢着,你这是背书啊!我想听的是你真正理解的流程。比如说,DispatcherServlet怎么知道这个请求应该交给哪个Controller?它又是怎么调用Controller的方法的?Controller返回的结果又是如何变成用户看到的页面的?”
小李(额头冒汗):“这个…我需要好好整理一下思路…”
老王(笑了笑):“哈哈,看来你还需要深入了解一下Spring MVC的内部机制。今天我们就来彻底剖析一下这个经典的请求处理流程吧!”
Spring MVC是Spring Framework的一个重要模块,它基于经典的MVC(Model-View-Controller)设计模式,为Web应用开发提供了优雅的解决方案。
Spring MVC的核心组件就像一个精密的乐队,每个组件都有自己的职责:
当一个HTTP请求到达Spring MVC应用时,整个处理流程如下:
DispatcherServlet是Spring MVC的心脏,继承自FrameworkServlet
,是整个请求处理流程的入口点。
让我们深入分析DispatcherServlet.doDispatch()
方法的源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1. 检查是否为文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 查找当前请求的处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 执行拦截器的前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 4. 获取处理器适配器并执行处理器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 5. 应用默认视图名称
applyDefaultViewName(processedRequest, mv);
// 6. 执行拦截器的后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new ServletException("Handler dispatch failed: " + err, err);
}
// 7. 处理分发结果(渲染视图或处理异常)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// ... 异常处理和资源清理
}
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
// 处理文件上传请求,包装为MultipartHttpServletRequest
return this.multipartResolver.resolveMultipart(request);
}
return request;
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
HandlerMapping负责将请求URL映射到具体的处理器。Spring MVC提供了多种实现:
public interface HandlerMapping {
/**
* 返回请求的处理器和拦截器链
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
/**
* 是否使用PathPattern进行路径匹配
*/
default boolean usesPathPatterns() {
return false;
}
}
@RequestMapping
注解以@RequestMapping("/user/{id}")
为例:
@Controller
public class UserController {
@RequestMapping("/user/{id}")
public ModelAndView getUser(@PathVariable Long id) {
// 业务逻辑处理
ModelAndView mv = new ModelAndView("user-detail");
mv.addObject("user", userService.findById(id));
return mv;
}
}
当请求/user/123
到达时,RequestMappingHandlerMapping
会:
{id} = 123
HandlerExecutionChain
HandlerAdapter采用适配器模式,使DispatcherServlet能够调用任何类型的处理器。
public interface HandlerAdapter {
/**
* 判断是否支持给定的处理器
*/
boolean supports(Object handler);
/**
* 使用给定的处理器处理请求
*/
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
}
@RequestMapping
注解的方法@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查请求方法
checkRequest(request);
// 调用处理器方法
mav = invokeHandlerMethod(request, response, handlerMethod);
// 处理缓存控制
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
Controller是业务逻辑的载体,Spring MVC支持多种Controller编写方式。
@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
log.info("获取用户信息:{}", id);
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
log.info("创建用户:{}", user);
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
}
Spring MVC通过HandlerMethodArgumentResolver
和HandlerMethodReturnValueHandler
来处理方法参数和返回值:
// 参数解析器示例
public class PathVariableMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(PathVariable.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
PathVariable pathVar = parameter.getParameterAnnotation(PathVariable.class);
String name = pathVar.value();
// 从请求中提取路径变量值
Map<String, String> uriTemplateVars =
(Map<String, String>) webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return uriTemplateVars.get(name);
}
}
ViewResolver负责将逻辑视图名解析为具体的View对象:
public interface ViewResolver {
/**
* 根据视图名称和区域信息解析视图
*/
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
// DispatcherServlet中的视图解析逻辑
protected void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 确定区域信息
Locale locale = (this.localeResolver != null ?
this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 解析视图名称
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" +
mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
}
}
else {
// 直接使用View对象
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv +
"] neither contains a view name nor a View object");
}
}
// 渲染视图
try {
if (mv.getStatus() != null) {
request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus());
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
public interface View {
/**
* 渲染视图
*/
void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception;
/**
* 获取内容类型
*/
@Nullable
default String getContentType() {
return null;
}
}
Spring MVC提供了统一的异常处理机制:
// DispatcherServlet中的异常处理逻辑
protected ModelAndView processHandlerException(HttpServletRequest request,
HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// 应用默认视图名称
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
log.error("用户未找到:{}", ex.getMessage());
ErrorResponse error = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ErrorResponse> handleValidation(ValidationException ex) {
log.error("参数验证失败:{}", ex.getMessage());
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
log.error("系统异常:", ex);
ErrorResponse error = new ErrorResponse("INTERNAL_ERROR", "系统内部错误");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
public interface HandlerInterceptor {
/**
* 前置处理:在Controller方法执行前调用
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return true;
}
/**
* 后置处理:在Controller方法执行后,视图渲染前调用
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 完成处理:在整个请求完成后调用
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, @Nullable Exception ex) throws Exception {
}
}
@Component
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (StringUtils.isEmpty(token)) {
log.warn("请求缺少认证令牌:{}", request.getRequestURI());
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
// 验证令牌
if (!isValidToken(token)) {
log.warn("无效的认证令牌:{}", token);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
// 设置用户上下文
User user = getUserFromToken(token);
UserContext.setCurrentUser(user);
log.info("用户认证成功:{}", user.getUsername());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 清理用户上下文
UserContext.clear();
}
private boolean isValidToken(String token) {
// 令牌验证逻辑
return true;
}
private User getUserFromToken(String token) {
// 从令牌中解析用户信息
return new User();
}
}
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthenticationInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login", "/api/register");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
}
@RestController
@RequestMapping("/api/users")
@Validated
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(
@PathVariable @Min(1) Long id,
@RequestParam(required = false) String fields) {
log.info("查询用户信息 - ID: {}, Fields: {}", id, fields);
User user = userService.findById(id);
if (user == null) {
throw new UserNotFoundException("用户不存在: " + id);
}
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(
@RequestBody @Valid CreateUserRequest request) {
log.info("创建用户 - Request: {}", request);
User user = new User();
BeanUtils.copyProperties(request, user);
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@RequestBody @Valid UpdateUserRequest request) {
log.info("更新用户 - ID: {}, Request: {}", id, request);
User existingUser = userService.findById(id);
if (existingUser == null) {
throw new UserNotFoundException("用户不存在: " + id);
}
BeanUtils.copyProperties(request, existingUser);
User updatedUser = userService.update(existingUser);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
log.info("删除用户 - ID: {}", id);
if (!userService.existsById(id)) {
throw new UserNotFoundException("用户不存在: " + id);
}
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
}
server:
tomcat:
max-connections: 8192
accept-count: 1000
max-threads: 800
min-spare-threads: 100
server:
compression:
enabled: true
mime-types: text/html,text/css,application/javascript,application/json
min-response-size: 1024
@GetMapping("/users/{id}")
@Cacheable(value = "users", key = "#id")
public User getUserById(@PathVariable Long id) {
return userService.findById(id);
}
@ControllerAdvice
进行全局异常处理经过详细的分析,小李终于明白了Spring MVC请求处理流程的精髓。
老王(满意地点头):“不错不错!现在你能完整地描述一下这个流程了吗?”
小李(信心满满):"当然可以!当用户请求 http://localhost:8080/user/123
时:
RequestMappingHandlerMapping
会根据@RequestMapping("/user/{id}")
找到对应的Controller方法RequestMappingHandlerAdapter
解析路径参数id=123
,调用Controller方法整个过程中,拦截器在适当的时机执行前置、后置和完成方法,异常处理器负责处理各种异常情况。"
老王(拍手叫好):“Perfect!这就是我想听到的答案。Spring MVC的设计真正体现了面向对象的精髓:单一职责、开闭原则、依赖倒置。每个组件各司其职,通过接口进行协作,既保证了灵活性,又确保了可扩展性。”
小李:“是的,这种设计让我们可以很容易地自定义各个组件,比如自定义HandlerMapping来支持特殊的URL规则,或者自定义ViewResolver来支持新的模板引擎。”
老王(欣慰地笑了):“看来你不仅理解了流程,更理解了设计思想。这样的理解才是真正的技术功底!记住,作为一名优秀的Java工程师,不仅要知其然,更要知其所以然。Spring MVC的源码就是最好的教科书,多读源码,多思考设计模式的应用,你会受益无穷的!”
从这场面试中可以看出,真正掌握Spring MVC不仅仅是会使用注解,而是要深入理解其内部机制、设计思想和最佳实践。只有这样,我们才能在遇到复杂问题时游刃有余,在系统设计时胸有成竹。
总结:Spring MVC通过精巧的组件设计和清晰的职责分离,为我们提供了一个强大而灵活的Web开发框架。理解其请求处理流程不仅有助于更好地使用框架,更能提升我们的架构设计能力。正如老王所说,知其然更要知其所以然,这才是技术成长的正确道路。