⭐ 掌握核心组件 | 💡 理解映射原理 | 🔥 面试必备知识
📖 前言
💭 「HandlerMapping和HandlerAdapter是SpringMVC的大脑和双手」
在SpringMVC的处理流程中,HandlerMapping和HandlerAdapter是两个至关重要的核心组件。如果说DispatcherServlet是交通警察,那么:
- HandlerMapping 就是GPS导航系统,负责找到目的地(Controller)
- HandlerAdapter 就是万能适配器,负责让不同类型的Controller都能正常工作
想象一下:当用户访问 /user/list 时,SpringMVC如何知道应该调用哪个Controller的哪个方法?这就是HandlerMapping的工作!找到方法后,如何执行它?这就是HandlerAdapter的工作!
🎯 学习目标:
- ✅ 深入理解HandlerMapping的工作原理
- ✅ 掌握HandlerAdapter的适配机制
- ✅ 理解不同类型的映射器和适配器
- ✅ 掌握URL映射规则和路径匹配
- ✅ 学会自定义映射器和适配器
- ✅ 应对面试高频考点
一、🎯 HandlerMapping详解
1.1 什么是HandlerMapping?
💡 通俗解释:HandlerMapping就像一本通讯录,根据URL找到对应的处理器(Controller)
🔍 定义:HandlerMapping是SpringMVC中的处理器映射器,负责根据请求的URL找到对应的Handler(处理器)。
用户请求
/user/list
HandlerMapping
处理器映射器
查找映射表
找到Handler
UserController.getUserList
返回HandlerExecutionChain
1.2 HandlerMapping的核心接口
java
/**
* HandlerMapping接口
* SpringMVC的核心接口之一
*/
public interface HandlerMapping {
/**
* 根据请求获取Handler
*
* @param request HTTP请求对象
* @return HandlerExecutionChain 处理器执行链(包含Handler和拦截器)
* @throws Exception 如果没有找到Handler
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
🌟 工作流程:
java
/**
* HandlerMapping的工作流程示意
*/
public class HandlerMappingWorkflow {
/**
* 第1步:接收请求
* 用户访问:http://localhost:8080/user/list
*/
void step1_ReceiveRequest() {
HttpServletRequest request = ...; // 请求对象
String url = request.getRequestURI(); // 获取URL:/user/list
}
/**
* 第2步:查找Handler
* HandlerMapping维护了URL和Handler的映射关系
*/
void step2_FindHandler() {
// 映射表(简化示意)
Map<String, HandlerMethod> mappings = new HashMap<>();
mappings.put("/user/list", new HandlerMethod(UserController.class, "getUserList"));
mappings.put("/user/{id}", new HandlerMethod(UserController.class, "getUser"));
// 根据URL查找
HandlerMethod handler = mappings.get("/user/list");
}
/**
* 第3步:创建执行链
* 包含Handler和拦截器
*/
void step3_CreateChain() {
HandlerExecutionChain chain = new HandlerExecutionChain(handler);
// 添加拦截器
chain.addInterceptor(new LoginInterceptor());
chain.addInterceptor(new PermissionInterceptor());
}
/**
* 第4步:返回执行链
* 交给DispatcherServlet继续处理
*/
void step4_ReturnChain() {
return chain;
}
}
1.3 HandlerMapping的常见实现类
SpringMVC提供了多种HandlerMapping实现,适用于不同场景:
| 实现类 | 说明 | 使用场景 | 优先级 |
|---|---|---|---|
| RequestMappingHandlerMapping | 处理@RequestMapping注解 | 现代开发(推荐) | 最高 |
| BeanNameUrlHandlerMapping | 根据Bean名称匹配 | 传统开发 | 中 |
| SimpleUrlHandlerMapping | 简单URL映射 | 静态资源 | 低 |
1.3.1 RequestMappingHandlerMapping(最常用)
java
/**
* RequestMappingHandlerMapping
* 处理@RequestMapping注解的映射器
*
* 核心功能:
* 1. 扫描所有@Controller类
* 2. 解析@RequestMapping注解
* 3. 建立URL和HandlerMethod的映射关系
*/
public class RequestMappingHandlerMappingDemo {
/**
* 映射信息存储结构(简化)
*/
private Map<RequestMappingInfo, HandlerMethod> mappingRegistry = new HashMap<>();
/**
* 初始化:扫描并注册映射
*/
public void initMapping() {
// 1. 获取所有@Controller bean
List<Object> controllers = getControllerBeans();
// 2. 遍历每个Controller
for (Object controller : controllers) {
Class<?> controllerClass = controller.getClass();
// 3. 获取类级别的@RequestMapping
RequestMapping classMapping =
controllerClass.getAnnotation(RequestMapping.class);
String classPath = classMapping != null ? classMapping.value()[0] : "";
// 4. 遍历所有方法
for (Method method : controllerClass.getDeclaredMethods()) {
// 5. 获取方法级别的@RequestMapping
RequestMapping methodMapping =
method.getAnnotation(RequestMapping.class);
if (methodMapping != null) {
// 6. 组合完整路径
String fullPath = classPath + methodMapping.value()[0];
// 7. 创建RequestMappingInfo(包含URL、请求方法等)
RequestMappingInfo info = new RequestMappingInfo(
fullPath, // URL路径
methodMapping.method(), // GET/POST等
methodMapping.params(), // 请求参数条件
methodMapping.headers() // 请求头条件
);
// 8. 创建HandlerMethod(Controller的方法)
HandlerMethod handlerMethod = new HandlerMethod(
controller, // Controller实例
method // 方法对象
);
// 9. 注册映射
mappingRegistry.put(info, handlerMethod);
System.out.println("注册映射:" + fullPath + " -> " +
controller.getClass().getSimpleName() +
"." + method.getName());
}
}
}
}
/**
* 查找Handler
*/
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) {
// 1. 获取请求信息
String url = request.getRequestURI();
String method = request.getMethod();
// 2. 创建查找条件
RequestMappingInfo lookupInfo = new RequestMappingInfo(url, method);
// 3. 查找匹配的Handler
HandlerMethod handler = findHandler(lookupInfo, request);
if (handler == null) {
return null; // 没找到,返回404
}
// 4. 创建执行链(包含拦截器)
HandlerExecutionChain chain = new HandlerExecutionChain(handler);
// 5. 添加拦截器
chain.addInterceptors(getInterceptors());
return chain;
}
/**
* 查找匹配的Handler
* 支持精确匹配和路径变量匹配
*/
private HandlerMethod findHandler(RequestMappingInfo lookupInfo,
HttpServletRequest request) {
// 1. 精确匹配
if (mappingRegistry.containsKey(lookupInfo)) {
return mappingRegistry.get(lookupInfo);
}
// 2. 路径变量匹配(如/user/{id})
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry :
mappingRegistry.entrySet()) {
RequestMappingInfo info = entry.getKey();
// 检查路径是否匹配(支持通配符)
if (pathMatcher.match(info.getPath(), lookupInfo.getPath())) {
// 提取路径变量(如id=123)
Map<String, String> pathVariables =
extractPathVariables(info.getPath(), lookupInfo.getPath());
// 将路径变量存入request
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
pathVariables);
return entry.getValue();
}
}
return null; // 没找到
}
/**
* 提取路径变量
* /user/{id} 匹配 /user/123 -> {id=123}
*/
private Map<String, String> extractPathVariables(String pattern, String path) {
Map<String, String> variables = new HashMap<>();
// 简化示例:实际使用AntPathMatcher
// pattern: /user/{id}
// path: /user/123
// 提取: id=123
String[] patternParts = pattern.split("/");
String[] pathParts = path.split("/");
for (int i = 0; i < patternParts.length; i++) {
String patternPart = patternParts[i];
if (patternPart.startsWith("{") && patternPart.endsWith("}")) {
// 提取变量名:{id} -> id
String varName = patternPart.substring(1, patternPart.length() - 1);
// 获取对应的值:123
String varValue = pathParts[i];
variables.put(varName, varValue);
}
}
return variables;
}
}
实际使用示例:
java
/**
* 用户Controller
* RequestMappingHandlerMapping会扫描这个类
*/
@Controller
@RequestMapping("/user") // 类级别:所有方法的URL前缀
public class UserController {
@Autowired
private UserService userService;
/**
* 映射1:GET /user/list
* 查询用户列表
*/
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String getUserList(Model model) {
List<User> users = userService.findAll();
model.addAttribute("users", users);
return "userList";
}
/**
* 映射2:GET /user/{id}
* 根据ID查询用户(路径变量)
*/
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getUser(@PathVariable Long id, Model model) {
// @PathVariable会从request的URI_TEMPLATE_VARIABLES_ATTRIBUTE中获取
User user = userService.findById(id);
model.addAttribute("user", user);
return "userDetail";
}
/**
* 映射3:POST /user/save
* 保存用户
*/
@RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveUser(User user) {
userService.save(user);
return "redirect:/user/list";
}
/**
* 映射4:带参数条件的映射
* GET /user/search?type=admin
* 只有当请求参数包含type=admin时才匹配
*/
@RequestMapping(value = "/search",
method = RequestMethod.GET,
params = "type=admin") // 参数条件
public String searchAdmin(Model model) {
List<User> admins = userService.findByType("admin");
model.addAttribute("users", admins);
return "userList";
}
/**
* 映射5:带请求头条件的映射
* 只有当请求头包含Content-Type=application/json时才匹配
*/
@RequestMapping(value = "/api/list",
headers = "Content-Type=application/json")
@ResponseBody
public List<User> getApiUserList() {
return userService.findAll();
}
}
RequestMappingInfo类(映射信息):
java
/**
* RequestMappingInfo
* 存储@RequestMapping注解的所有信息
*/
public class RequestMappingInfo {
// URL路径
private String path;
// HTTP请求方法(GET、POST等)
private RequestMethod[] methods;
// 请求参数条件(如params="type=admin")
private String[] params;
// 请求头条件(如headers="Content-Type=application/json")
private String[] headers;
// 构造函数
public RequestMappingInfo(String path, RequestMethod[] methods,
String[] params, String[] headers) {
this.path = path;
this.methods = methods;
this.params = params;
this.headers = headers;
}
/**
* 判断请求是否匹配
*/
public boolean match(HttpServletRequest request) {
// 1. 检查路径
if (!matchPath(request.getRequestURI())) {
return false;
}
// 2. 检查请求方法
if (!matchMethod(request.getMethod())) {
return false;
}
// 3. 检查参数条件
if (!matchParams(request)) {
return false;
}
// 4. 检查请求头条件
if (!matchHeaders(request)) {
return false;
}
return true; // 全部匹配
}
// 各种匹配方法...
}
1.3.2 BeanNameUrlHandlerMapping(传统方式)
java
/**
* BeanNameUrlHandlerMapping
* 根据Bean的名称匹配URL
*
* Bean名称必须以"/"开头
*/
/**
* 配置示例
*/
@Configuration
public class HandlerMappingConfig {
/**
* Bean名称就是URL
* Bean名:/user -> URL:/user
*/
@Bean("/user")
public Controller userController() {
return new Controller() {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView();
mv.setViewName("user");
return mv;
}
};
}
/**
* 多个URL映射到同一个Controller
*/
@Bean(name = {"/product", "/goods"})
public Controller productController() {
return (request, response) -> {
ModelAndView mv = new ModelAndView();
mv.setViewName("product");
return mv;
};
}
}
1.3.3 SimpleUrlHandlerMapping(配置式映射)
java
/**
* SimpleUrlHandlerMapping
* 通过配置建立URL和Handler的映射
*/
@Configuration
public class SimpleUrlMappingConfig {
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
// 配置URL和Handler的映射
Map<String, Object> urlMap = new HashMap<>();
urlMap.put("/welcome", welcomeController());
urlMap.put("/about", aboutController());
urlMap.put("/contact", contactController());
mapping.setUrlMap(urlMap);
mapping.setOrder(1); // 设置优先级
return mapping;
}
@Bean
public Controller welcomeController() {
return (request, response) -> {
ModelAndView mv = new ModelAndView();
mv.setViewName("welcome");
return mv;
};
}
@Bean
public Controller aboutController() {
return (request, response) -> {
ModelAndView mv = new ModelAndView();
mv.setViewName("about");
return mv;
};
}
@Bean
public Controller contactController() {
return (request, response) -> {
ModelAndView mv = new ModelAndView();
mv.setViewName("contact");
return mv;
};
}
}
1.4 URL路径匹配规则
java
/**
* URL路径匹配规则
* SpringMVC使用AntPathMatcher进行路径匹配
*/
public class UrlPatternMatching {
/**
* 精确匹配
*/
void exactMatch() {
// 模式:/user/list
// 匹配:/user/list ✅
// 不匹配:/user/list/1 ❌
// 不匹配:/user ❌
}
/**
* ? 匹配单个字符
*/
void questionMarkMatch() {
// 模式:/user/?
// 匹配:/user/1 ✅
// 匹配:/user/a ✅
// 不匹配:/user/12 ❌(两个字符)
// 不匹配:/user/ ❌(没有字符)
}
/**
* * 匹配0个或多个字符(不包括/)
*/
void starMatch() {
// 模式:/user/*
// 匹配:/user/list ✅
// 匹配:/user/1 ✅
// 匹配:/user/abc ✅
// 不匹配:/user/list/1 ❌(包含/)
}
/**
* ** 匹配0个或多个路径段
*/
void doubleStarMatch() {
// 模式:/user/**
// 匹配:/user/list ✅
// 匹配:/user/list/1 ✅
// 匹配:/user/a/b/c ✅
// 匹配:/user/ ✅
// 模式:/**/list
// 匹配:/user/list ✅
// 匹配:/admin/user/list ✅
// 匹配:/a/b/c/list ✅
}
/**
* {变量名} 路径变量
*/
void pathVariableMatch() {
// 模式:/user/{id}
// 匹配:/user/123 ✅ (id=123)
// 匹配:/user/abc ✅ (id=abc)
// 不匹配:/user/123/edit ❌
// 模式:/user/{id}/order/{orderId}
// 匹配:/user/123/order/456 ✅ (id=123, orderId=456)
}
/**
* 正则表达式路径变量
*/
void regexPathVariableMatch() {
// 模式:/user/{id:\\d+} (id必须是数字)
// 匹配:/user/123 ✅
// 不匹配:/user/abc ❌(不是数字)
// 模式:/file/{filename:.+\\.jpg} (文件名必须以.jpg结尾)
// 匹配:/file/photo.jpg ✅
// 不匹配:/file/photo.png ❌
}
}
实际应用示例:
java
@Controller
@RequestMapping("/api")
public class AdvancedMappingController {
/**
* 精确匹配
* URL: /api/users
*/
@GetMapping("/users")
public String getUsers() {
return "users";
}
/**
* 路径变量(基础)
* URL: /api/users/123
*/
@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id) {
return "user";
}
/**
* 路径变量(正则约束)
* URL: /api/users/123(id必须是数字)
*/
@GetMapping("/users/{id:\\d+}")
public String getUserWithRegex(@PathVariable Long id) {
return "user";
}
/**
* 多个路径变量
* URL: /api/users/123/orders/456
*/
@GetMapping("/users/{userId}/orders/{orderId}")
public String getUserOrder(@PathVariable Long userId,
@PathVariable Long orderId) {
return "order";
}
/**
* 通配符匹配
* URL: /api/download/file/report.pdf
* URL: /api/download/image/logo.png
*/
@GetMapping("/download/**")
public void downloadFile(HttpServletRequest request) {
// 获取完整路径
String fullPath = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
);
// fullPath = /api/download/file/report.pdf
// 提取文件路径
String filePath = fullPath.substring("/api/download/".length());
// filePath = file/report.pdf
}
/**
* Ant风格路径
* URL: /api/files/report.pdf
* URL: /api/files/data.xlsx
*/
@GetMapping("/files/*.{extension}")
public String getFile(@PathVariable String extension) {
return "file";
}
}
1.5 自定义HandlerMapping
java
/**
* 自定义HandlerMapping示例
* 实现基于版本号的API映射
*
* URL格式:/v1/user/list, /v2/user/list
*/
public class VersionHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
HttpServletRequest request) {
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
// 从URL中提取版本号
String uri = request.getRequestURI();
Pattern pattern = Pattern.compile("/v(\\d+)/.*");
Matcher matcher = pattern.matcher(uri);
if (matcher.matches()) {
String version = matcher.group(1);
System.out.println("API版本:v" + version);
// 将版本号存入request
request.setAttribute("apiVersion", version);
// 可以根据版本号添加不同的拦截器
if ("1".equals(version)) {
chain.addInterceptor(new V1ApiInterceptor());
} else if ("2".equals(version)) {
chain.addInterceptor(new V2ApiInterceptor());
}
}
return chain;
}
}
/**
* 注册自定义HandlerMapping
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public VersionHandlerMapping versionHandlerMapping() {
return new VersionHandlerMapping();
}
}
/**
* 使用版本化API
*/
@RestController
public class ApiController {
/**
* V1版本API
* URL: /v1/users
*/
@GetMapping("/v1/users")
public Result getUsersV1() {
return Result.success("这是V1版本");
}
/**
* V2版本API
* URL: /v2/users
*/
@GetMapping("/v2/users")
public Result getUsersV2() {
return Result.success("这是V2版本,功能更强大");
}
}
二、🔌 HandlerAdapter详解
2.1 什么是HandlerAdapter?
💡 通俗解释:HandlerAdapter就像万能充电器,让不同类型的Controller都能正常工作
🔍 定义:HandlerAdapter是SpringMVC中的处理器适配器,负责执行Handler(Controller方法)并返回ModelAndView。
渲染错误: Mermaid 渲染失败: Parse error on line 4: ...roller类型] B --> D[@RequestMapping] ----------------------^ Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'LINK_ID'
为什么需要HandlerAdapter?
java
/**
* 问题:不同类型的Handler有不同的执行方式
*/
// 类型1:实现Controller接口
public class OldStyleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
return new ModelAndView("view");
}
}
// 类型2:使用@RequestMapping注解
@Controller
public class NewStyleController {
@RequestMapping("/user")
public String getUser() {
return "user";
}
}
// 类型3:实现Servlet
public class ServletStyleHandler extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// ...
}
}
/**
* DispatcherServlet如何统一调用这些不同类型的Handler?
* 答案:使用HandlerAdapter适配器模式!
*/
2.2 HandlerAdapter的核心接口
java
/**
* HandlerAdapter接口
*/
public interface HandlerAdapter {
/**
* 判断是否支持该Handler
*
* @param handler Handler对象
* @return true表示支持,false表示不支持
*/
boolean supports(Object handler);
/**
* 执行Handler
*
* @param request HTTP请求
* @param response HTTP响应
* @param handler Handler对象
* @return ModelAndView
* @throws Exception 执行异常
*/
@Nullable
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
/**
* 获取Last-Modified时间
* 用于缓存控制
*/
long getLastModified(HttpServletRequest request, Object handler);
}
2.3 HandlerAdapter的常见实现类
| 实现类 | 支持的Handler类型 | 说明 |
|---|---|---|
| RequestMappingHandlerAdapter | @RequestMapping注解的方法 | 现代开发(推荐) |
| SimpleControllerHandlerAdapter | 实现Controller接口的类 | 传统开发 |
| HttpRequestHandlerAdapter | 实现HttpRequestHandler接口 | 处理HTTP请求 |
| SimpleServletHandlerAdapter | Servlet类 | 集成Servlet |
2.3.1 RequestMappingHandlerAdapter(最常用)
java
/**
* RequestMappingHandlerAdapter
* 处理@RequestMapping注解的方法
*
* 核心功能:
* 1. 准备方法参数(参数解析)
* 2. 反射调用Controller方法
* 3. 处理返回值
*/
public class RequestMappingHandlerAdapterDemo {
// 参数解析器列表
private List<HandlerMethodArgumentResolver> argumentResolvers;
// 返回值处理器列表
private List<HandlerMethodReturnValueHandler> returnValueHandlers;
/**
* 判断是否支持该Handler
*/
@Override
public boolean supports(Object handler) {
// 只支持HandlerMethod类型(@RequestMapping注解的方法)
return handler instanceof HandlerMethod;
}
/**
* 执行Handler
*/
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// ===== 第1步:准备方法参数 =====
Object[] args = resolveArguments(handlerMethod, request, response);
// ===== 第2步:反射调用方法 =====
Object returnValue = invokeMethod(handlerMethod, args);
// ===== 第3步:处理返回值 =====
ModelAndView mv = handleReturnValue(returnValue, handlerMethod, request, response);
return mv;
}
/**
* 第1步:解析方法参数
* 核心难点!需要根据参数注解和类型,从request中提取值
*/
private Object[] resolveArguments(HandlerMethod handlerMethod,
HttpServletRequest request,
HttpServletResponse response) {
// 1. 获取方法的所有参数
MethodParameter[] parameters = handlerMethod.getMethodParameters();
Object[] args = new Object[parameters.length];
// 2. 遍历每个参数,逐个解析
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 3. 查找能处理该参数的解析器
HandlerMethodArgumentResolver resolver =
findArgumentResolver(parameter);
if (resolver != null) {
// 4. 使用解析器解析参数值
args[i] = resolver.resolveArgument(
parameter,
request,
response
);
} else {
throw new IllegalStateException(
"无法解析参数:" + parameter.getParameterName()
);
}
}
return args;
}
/**
* 查找参数解析器
*/
private HandlerMethodArgumentResolver findArgumentResolver(
MethodParameter parameter) {
// 遍历所有解析器,找到支持该参数的解析器
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
return resolver;
}
}
return null;
}
/**
* 第2步:反射调用方法
*/
private Object invokeMethod(HandlerMethod handlerMethod, Object[] args)
throws Exception {
// 1. 获取Controller实例
Object controller = handlerMethod.getBean();
// 2. 获取Method对象
Method method = handlerMethod.getMethod();
// 3. 设置可访问(如果是private方法)
method.setAccessible(true);
// 4. 反射调用
try {
Object returnValue = method.invoke(controller, args);
return returnValue;
} catch (InvocationTargetException e) {
// 如果方法内部抛异常,获取真正的异常
throw (Exception) e.getTargetException();
}
}
/**
* 第3步:处理返回值
*/
private ModelAndView handleReturnValue(Object returnValue,
HandlerMethod handlerMethod,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// 1. 检查方法是否有@ResponseBody注解
if (handlerMethod.hasMethodAnnotation(ResponseBody.class)) {
// 直接写入响应体(JSON)
writeResponse(returnValue, response);
return null; // 不需要视图
}
// 2. 根据返回值类型处理
if (returnValue instanceof String) {
// 返回视图名称
String viewName = (String) returnValue;
ModelAndView mv = new ModelAndView();
mv.setViewName(viewName);
return mv;
} else if (returnValue instanceof ModelAndView) {
// 直接返回ModelAndView
return (ModelAndView) returnValue;
} else if (returnValue == null) {
// void方法或返回null
return null;
} else {
// 其他类型,尝试添加到Model
ModelAndView mv = new ModelAndView();
mv.addObject(returnValue);
return mv;
}
}
/**
* 将对象写入响应体(JSON格式)
*/
private void writeResponse(Object returnValue, HttpServletResponse response)
throws IOException {
response.setContentType("application/json;charset=UTF-8");
// 使用Jackson转换为JSON
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(returnValue);
response.getWriter().write(json);
}
}
2.4 参数解析器(ArgumentResolver)
💡 核心难点:如何将HTTP请求中的数据绑定到Controller方法的参数上?
java
/**
* 参数解析器接口
*/
public interface HandlerMethodArgumentResolver {
/**
* 判断是否支持该参数
*/
boolean supportsParameter(MethodParameter parameter);
/**
* 解析参数值
*/
@Nullable
Object resolveArgument(MethodParameter parameter,
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
常见的参数解析器
java
/**
* 1. @RequestParam解析器
* 解析请求参数
*/
public class RequestParamMethodArgumentResolver
implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 支持有@RequestParam注解的参数
return parameter.hasParameterAnnotation(RequestParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
HttpServletRequest request,
HttpServletResponse response) {
// 1. 获取@RequestParam注解
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
// 2. 获取参数名
String paramName = ann.value();
if (paramName.isEmpty()) {
paramName = parameter.getParameterName(); // 使用方法参数名
}
// 3. 从request中获取值
String value = request.getParameter(paramName);
// 4. 检查required属性
if (value == null && ann.required()) {
throw new IllegalArgumentException(
"缺少必需参数:" + paramName
);
}
// 5. 使用默认值
if (value == null) {
value = ann.defaultValue();
}
// 6. 类型转换
Class<?> paramType = parameter.getParameterType();
return convertValue(value, paramType);
}
/**
* 类型转换
*/
private Object convertValue(String value, Class<?> targetType) {
if (targetType == String.class) {
return value;
} else if (targetType == Integer.class || targetType == int.class) {
return Integer.parseInt(value);
} else if (targetType == Long.class || targetType == long.class) {
return Long.parseLong(value);
} else if (targetType == Boolean.class || targetType == boolean.class) {
return Boolean.parseBoolean(value);
}
// ... 其他类型转换
return value;
}
}
/**
* 使用示例
*/
@Controller
public class ExampleController {
@RequestMapping("/search")
public String search(
@RequestParam("keyword") String keyword, // 必需参数
@RequestParam(value = "page", defaultValue = "1") int page, // 默认值
@RequestParam(required = false) String sort) { // 可选参数
// keyword: 从request.getParameter("keyword")获取
// page: 如果没有,使用默认值1
// sort: 可以为null
return "searchResult";
}
}
java
/**
* 2. @PathVariable解析器
* 解析路径变量
*/
public class PathVariableMethodArgumentResolver
implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(PathVariable.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
HttpServletRequest request,
HttpServletResponse response) {
// 1. 获取@PathVariable注解
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
// 2. 获取变量名
String varName = ann.value();
if (varName.isEmpty()) {
varName = parameter.getParameterName();
}
// 3. 从request的attribute中获取路径变量
// HandlerMapping已经提取并存储在request中
@SuppressWarnings("unchecked")
Map<String, String> uriVariables = (Map<String, String>)
request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
// 4. 获取值
String value = uriVariables.get(varName);
if (value == null) {
throw new IllegalArgumentException(
"缺少路径变量:" + varName
);
}
// 5. 类型转换
Class<?> paramType = parameter.getParameterType();
return convertValue(value, paramType);
}
}
/**
* 使用示例
*/
@Controller
public class UserController {
@RequestMapping("/user/{id}")
public String getUser(@PathVariable Long id, Model model) {
// id从URL路径中提取
// URL: /user/123 -> id=123
User user = userService.findById(id);
model.addAttribute("user", user);
return "userDetail";
}
@RequestMapping("/user/{userId}/order/{orderId}")
public String getUserOrder(@PathVariable Long userId,
@PathVariable Long orderId,
Model model) {
// URL: /user/123/order/456
// userId=123, orderId=456
Order order = orderService.findByUserAndId(userId, orderId);
model.addAttribute("order", order);
return "orderDetail";
}
}
java
/**
* 3. @RequestBody解析器
* 解析JSON请求体
*/
public class RequestBodyMethodArgumentResolver
implements HandlerMethodArgumentResolver {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 1. 读取请求体
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
// 2. JSON反序列化为Java对象
Class<?> paramType = parameter.getParameterType();
Object value = objectMapper.readValue(json, paramType);
return value;
}
}
/**
* 使用示例
*/
@RestController
@RequestMapping("/api/user")
public class UserRestController {
@PostMapping
public Result createUser(@RequestBody User user) {
// 请求头:Content-Type: application/json
// 请求体:{"name":"张三","age":25,"email":"zhang@example.com"}
// 自动反序列化为User对象
userService.save(user);
return Result.success(user);
}
}
java
/**
* 4. Model和ModelMap解析器
* 提供Model对象用于存放数据
*/
public class ModelMethodArgumentResolver
implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return Model.class.isAssignableFrom(paramType) ||
ModelMap.class.isAssignableFrom(paramType);
}
@Override
public Object resolveArgument(MethodParameter parameter,
HttpServletRequest request,
HttpServletResponse response) {
// 从request中获取或创建Model
ModelMap model = (ModelMap) request.getAttribute(MODEL_ATTRIBUTE);
if (model == null) {
model = new ModelMap();
request.setAttribute(MODEL_ATTRIBUTE, model);
}
return model;
}
}
/**
* 使用示例
*/
@Controller
public class ProductController {
@RequestMapping("/product/list")
public String getProductList(Model model) {
// Spring自动提供Model对象
List<Product> products = productService.findAll();
// 将数据放入Model
model.addAttribute("products", products);
model.addAttribute("totalCount", products.size());
return "productList";
}
}
java
/**
* 5. HttpServletRequest和HttpServletResponse解析器
* 直接提供Servlet API对象
*/
public class ServletRequestMethodArgumentResolver
implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return HttpServletRequest.class.isAssignableFrom(paramType) ||
HttpServletResponse.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType);
}
@Override
public Object resolveArgument(MethodParameter parameter,
HttpServletRequest request,
HttpServletResponse response) {
Class<?> paramType = parameter.getParameterType();
if (HttpServletRequest.class.isAssignableFrom(paramType)) {
return request;
} else if (HttpServletResponse.class.isAssignableFrom(paramType)) {
return response;
} else if (HttpSession.class.isAssignableFrom(paramType)) {
return request.getSession();
}
return null;
}
}
/**
* 使用示例
*/
@Controller
public class DownloadController {
@RequestMapping("/download")
public void download(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 直接操作request和response
String filename = request.getParameter("filename");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=" + filename);
// 写入文件数据...
OutputStream out = response.getOutputStream();
// ...
}
}
2.5 返回值处理器(ReturnValueHandler)
java
/**
* 返回值处理器接口
*/
public interface HandlerMethodReturnValueHandler {
/**
* 判断是否支持该返回值类型
*/
boolean supportsReturnType(MethodParameter returnType);
/**
* 处理返回值
*/
void handleReturnValue(@Nullable Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
常见的返回值处理器:
java
/**
* 1. @ResponseBody返回值处理器
* 将返回值转换为JSON写入响应体
*/
public class ResponseBodyReturnValueHandler
implements HandlerMethodReturnValueHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 方法或类上有@ResponseBody注解
return returnType.hasMethodAnnotation(ResponseBody.class) ||
returnType.getContainingClass().isAnnotationPresent(RestController.class);
}
@Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 1. 设置响应类型
response.setContentType("application/json;charset=UTF-8");
// 2. 转换为JSON
String json = objectMapper.writeValueAsString(returnValue);
// 3. 写入响应体
response.getWriter().write(json);
// 4. 标记不需要视图
mavContainer.setRequestHandled(true);
}
}
/**
* 使用示例
*/
@RestController
public class ApiController {
@GetMapping("/api/users")
public List<User> getUsers() {
// 返回的List会自动转换为JSON
return userService.findAll();
}
}
2.6 完整的请求处理流程
java
/**
* HandlerMapping和HandlerAdapter协作流程
*/
public class CompleteWorkflow {
/**
* 完整流程演示
*/
public void processRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// ===== 第1步:HandlerMapping查找Handler =====
HandlerExecutionChain chain = null;
// 遍历所有HandlerMapping
for (HandlerMapping mapping : handlerMappings) {
chain = mapping.getHandler(request);
if (chain != null) {
break; // 找到了就停止
}
}
if (chain == null) {
// 没找到Handler,返回404
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
Object handler = chain.getHandler();
System.out.println("找到Handler: " + handler);
// ===== 第2步:获取HandlerAdapter =====
HandlerAdapter adapter = null;
// 遍历所有HandlerAdapter
for (HandlerAdapter ha : handlerAdapters) {
if (ha.supports(handler)) {
adapter = ha;
break; // 找到支持的适配器
}
}
if (adapter == null) {
throw new ServletException("没有找到适配器for Handler: " + handler);
}
System.out.println("使用适配器: " + adapter.getClass().getSimpleName());
// ===== 第3步:执行拦截器的preHandle =====
if (!chain.applyPreHandle(request, response)) {
return; // 拦截器返回false,中断请求
}
// ===== 第4步:执行Handler =====
ModelAndView mv = adapter.handle(request, response, handler);
// ===== 第5步:执行拦截器的postHandle =====
chain.applyPostHandle(request, response, mv);
// ===== 第6步:视图渲染 =====
if (mv != null && mv.hasView()) {
render(mv, request, response);
}
// ===== 第7步:执行拦截器的afterCompletion =====
chain.triggerAfterCompletion(request, response, null);
}
/**
* 渲染视图
*/
private void render(ModelAndView mv,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 1. 解析视图
View view = resolveViewName(mv.getViewName());
// 2. 渲染视图
view.render(mv.getModel(), request, response);
}
}
三、💻 实战案例
3.1 案例1:自定义参数解析器
需求:在Controller方法中直接获取当前登录用户对象
java
/**
* 自定义注解:标记当前用户参数
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser {
/**
* 是否必需登录
*/
boolean required() default true;
}
/**
* 当前用户参数解析器
*/
public class CurrentUserMethodArgumentResolver
implements HandlerMethodArgumentResolver {
/**
* 支持有@CurrentUser注解的User类型参数
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 1. 检查是否有@CurrentUser注解
// 2. 检查参数类型是否是User
return parameter.hasParameterAnnotation(CurrentUser.class) &&
parameter.getParameterType().equals(User.class);
}
/**
* 解析参数值:从session中获取当前用户
*/
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
// 1. 获取@CurrentUser注解
CurrentUser annotation = parameter.getParameterAnnotation(CurrentUser.class);
boolean required = annotation.required();
// 2. 从session中获取用户
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpSession session = request.getSession(false);
User user = null;
if (session != null) {
user = (User) session.getAttribute("currentUser");
}
// 3. 检查是否必需登录
if (user == null && required) {
throw new IllegalStateException("用户未登录!");
}
return user;
}
}
/**
* 注册自定义参数解析器
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// 添加自定义参数解析器
resolvers.add(new CurrentUserMethodArgumentResolver());
}
}
/**
* 使用示例
*/
@Controller
public class UserController {
/**
* 获取当前用户的个人资料
* 自动从session中获取用户,无需手动编写代码
*/
@GetMapping("/profile")
public String getProfile(@CurrentUser User currentUser, Model model) {
// currentUser已经自动从session中获取
// 如果未登录会抛出异常
System.out.println("当前用户:" + currentUser.getName());
model.addAttribute("user", currentUser);
return "profile";
}
/**
* 可选登录的页面
*/
@GetMapping("/home")
public String home(@CurrentUser(required = false) User currentUser,
Model model) {
// required=false,未登录也不会报错
if (currentUser != null) {
model.addAttribute("welcomeMessage", "欢迎," + currentUser.getName());
} else {
model.addAttribute("welcomeMessage", "欢迎访问");
}
return "home";
}
}
3.2 案例2:自定义返回值处理器
需求:统一封装API响应格式
java
/**
* 统一响应格式
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
private int code; // 状态码
private String message; // 提示信息
private T data; // 数据
private long timestamp; // 时间戳
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data, System.currentTimeMillis());
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(500, message, null, System.currentTimeMillis());
}
}
/**
* 自定义注解:标记需要包装的响应
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiResponseWrapper {
}
/**
* API响应包装处理器
*/
public class ApiResponseWrapperReturnValueHandler
implements HandlerMethodReturnValueHandler {
private final HandlerMethodReturnValueHandler delegate;
private final ObjectMapper objectMapper = new ObjectMapper();
public ApiResponseWrapperReturnValueHandler(
HandlerMethodReturnValueHandler delegate) {
this.delegate = delegate;
}
/**
* 支持有@ApiResponseWrapper注解的方法
*/
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return returnType.hasMethodAnnotation(ApiResponseWrapper.class) ||
returnType.getContainingClass().isAnnotationPresent(ApiResponseWrapper.class);
}
/**
* 包装返回值
*/
@Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
// 1. 如果返回值已经是ApiResponse,直接返回
if (returnValue instanceof ApiResponse) {
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
// 2. 包装返回值
ApiResponse<?> wrappedResponse = ApiResponse.success(returnValue);
// 3. 委托给原始处理器(如ResponseBodyReturnValueHandler)
delegate.handleReturnValue(wrappedResponse, returnType, mavContainer, webRequest);
}
}
/**
* 注册自定义返回值处理器
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
// 注意:需要在最前面插入,优先处理
handlers.add(0, new ApiResponseWrapperReturnValueHandler(
new RequestResponseBodyMethodProcessor(
Collections.singletonList(new MappingJackson2HttpMessageConverter())
)
));
}
}
/**
* 使用示例
*/
@RestController
@RequestMapping("/api")
@ApiResponseWrapper // 类级别注解,所有方法都会自动包装
public class UserApiController {
@Autowired
private UserService userService;
/**
* 返回User对象,自动包装为ApiResponse
*
* 原始返回:{"id":1,"name":"张三"}
* 包装后:{
* "code": 200,
* "message": "success",
* "data": {"id":1,"name":"张三"},
* "timestamp": 1700000000000
* }
*/
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
/**
* 返回List,自动包装为ApiResponse
*/
@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll();
}
/**
* 已经是ApiResponse类型,不会重复包装
*/
@PostMapping("/users")
public ApiResponse<User> createUser(@RequestBody User user) {
User saved = userService.save(user);
return ApiResponse.success(saved);
}
}
3.3 案例3:多版本API路由
需求:同一个URL支持多个版本的API
java
/**
* API版本注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
/**
* API版本号
*/
int value();
}
/**
* 自定义请求条件:版本号匹配
*/
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
private int apiVersion;
public ApiVersionCondition(int apiVersion) {
this.apiVersion = apiVersion;
}
/**
* 合并条件(方法级别覆盖类级别)
*/
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
return new ApiVersionCondition(other.apiVersion);
}
/**
* 从请求中获取匹配的条件
*/
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
// 从请求头获取版本号
String versionHeader = request.getHeader("API-Version");
if (versionHeader != null) {
int requestVersion = Integer.parseInt(versionHeader);
// 请求版本 >= API版本,则匹配
if (requestVersion >= this.apiVersion) {
return this;
}
}
return null; // 不匹配
}
/**
* 比较优先级(版本号高的优先)
*/
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
return other.apiVersion - this.apiVersion; // 版本高的排前面
}
}
/**
* 自定义HandlerMapping:支持API版本
*/
public class ApiVersionHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = handlerType.getAnnotation(ApiVersion.class);
return createCondition(apiVersion);
}
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
ApiVersion apiVersion = method.getAnnotation(ApiVersion.class);
return createCondition(apiVersion);
}
private RequestCondition<?> createCondition(ApiVersion apiVersion) {
return apiVersion == null ? null :
new ApiVersionCondition(apiVersion.value());
}
}
/**
* 注册自定义HandlerMapping
*/
@Configuration
public class WebMvcConfig {
@Bean
public ApiVersionHandlerMapping apiVersionHandlerMapping() {
return new ApiVersionHandlerMapping();
}
}
/**
* 使用示例
*/
@RestController
@RequestMapping("/api/users")
public class UserVersionController {
/**
* V1版本API
* Header: API-Version: 1
*/
@GetMapping
@ApiVersion(1)
public ApiResponse<List<UserV1>> getUsersV1() {
List<UserV1> users = new ArrayList<>();
users.add(new UserV1(1L, "张三"));
users.add(new UserV1(2L, "李四"));
return ApiResponse.success(users);
}
/**
* V2版本API(增加了邮箱字段)
* Header: API-Version: 2
*/
@GetMapping
@ApiVersion(2)
public ApiResponse<List<UserV2>> getUsersV2() {
List<UserV2> users = new ArrayList<>();
users.add(new UserV2(1L, "张三", "zhang@example.com"));
users.add(new UserV2(2L, "李四", "li@example.com"));
return ApiResponse.success(users);
}
/**
* V3版本API(增加了年龄字段)
* Header: API-Version: 3
*/
@GetMapping
@ApiVersion(3)
public ApiResponse<List<UserV3>> getUsersV3() {
List<UserV3> users = new ArrayList<>();
users.add(new UserV3(1L, "张三", "zhang@example.com", 25));
users.add(new UserV3(2L, "李四", "li@example.com", 30));
return ApiResponse.success(users);
}
}
/**
* 不同版本的User模型
*/
@Data
class UserV1 {
private Long id;
private String name;
}
@Data
class UserV2 extends UserV1 {
private String email;
}
@Data
class UserV3 extends UserV2 {
private Integer age;
}
测试示例:
bash
# V1版本
curl -H "API-Version: 1" http://localhost:8080/api/users
# 返回:[{"id":1,"name":"张三"},{"id":2,"name":"李四"}]
# V2版本
curl -H "API-Version: 2" http://localhost:8080/api/users
# 返回:[{"id":1,"name":"张三","email":"zhang@example.com"}...]
# V3版本
curl -H "API-Version: 3" http://localhost:8080/api/users
# 返回:[{"id":1,"name":"张三","email":"zhang@example.com","age":25}...]
四、📚 面试高频题
面试题1:什么是HandlerMapping?它的作用是什么?
点击查看答案
答案:
定义:
HandlerMapping是SpringMVC中的处理器映射器,负责根据请求的URL找到对应的Handler(处理器)。
核心作用:
- URL映射:建立URL和Controller方法的映射关系
- 查找Handler:根据请求URL查找对应的处理器
- 返回执行链:返回HandlerExecutionChain(包含Handler和拦截器)
工作流程:
java
// 1. 用户请求:GET /user/list
HttpServletRequest request = ...;
// 2. HandlerMapping查找Handler
HandlerExecutionChain chain = handlerMapping.getHandler(request);
// 3. 返回执行链
Handler handler = chain.getHandler(); // UserController.getUserList方法
List<Interceptor> interceptors = chain.getInterceptors(); // 拦截器列表
常用实现类:
- RequestMappingHandlerMapping:处理@RequestMapping注解(最常用)
- BeanNameUrlHandlerMapping:根据Bean名称匹配URL
- SimpleUrlHandlerMapping:配置式URL映射
核心接口方法:
java
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception;
}
面试题2:什么是HandlerAdapter?为什么需要它?
点击查看答案
答案:
定义:
HandlerAdapter是SpringMVC中的处理器适配器,负责执行Handler(Controller方法)并返回ModelAndView。
为什么需要HandlerAdapter?
**问题:**不同类型的Handler有不同的执行方式
java
// 类型1:实现Controller接口
public class OldController implements Controller {
public ModelAndView handleRequest(HttpServletRequest req,
HttpServletResponse resp) {
return new ModelAndView("view");
}
}
// 类型2:@RequestMapping注解
@Controller
public class NewController {
@RequestMapping("/user")
public String getUser() {
return "user";
}
}
// 类型3:Servlet
public class ServletHandler extends HttpServlet {
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) {
// ...
}
}
**解决方案:**使用适配器模式
java
// HandlerAdapter统一调用接口
public interface HandlerAdapter {
boolean supports(Object handler); // 是否支持该Handler
ModelAndView handle(..., Object handler); // 执行Handler
}
核心功能:
- 参数解析:从HTTP请求中提取参数,绑定到方法参数
- 方法调用:反射调用Controller方法
- 返回值处理:将返回值转换为ModelAndView
常用实现类:
- RequestMappingHandlerAdapter:处理@RequestMapping方法
- SimpleControllerHandlerAdapter:处理Controller接口
- HttpRequestHandlerAdapter:处理HttpRequestHandler接口
工作流程:
java
// 1. 查找适配器
HandlerAdapter adapter = null;
for (HandlerAdapter ha : handlerAdapters) {
if (ha.supports(handler)) {
adapter = ha;
break;
}
}
// 2. 执行Handler
ModelAndView mv = adapter.handle(request, response, handler);
面试题3:HandlerMapping和HandlerAdapter的协作流程是什么?
点击查看答案
答案:
完整流程(7步):
java
/**
* SpringMVC处理请求的完整流程
*/
public void processRequest(HttpServletRequest request,
HttpServletResponse response) {
// ===== 第1步:HandlerMapping查找Handler =====
HandlerExecutionChain chain = null;
for (HandlerMapping hm : handlerMappings) {
chain = hm.getHandler(request);
if (chain != null) break;
}
if (chain == null) {
response.sendError(404); // 未找到Handler
return;
}
Object handler = chain.getHandler();
// ===== 第2步:查找HandlerAdapter =====
HandlerAdapter adapter = null;
for (HandlerAdapter ha : handlerAdapters) {
if (ha.supports(handler)) {
adapter = ha;
break;
}
}
// ===== 第3步:执行拦截器preHandle =====
if (!chain.applyPreHandle(request, response)) {
return; // 拦截器返回false,中断请求
}
// ===== 第4步:HandlerAdapter执行Handler =====
ModelAndView mv = adapter.handle(request, response, handler);
// ===== 第5步:执行拦截器postHandle =====
chain.applyPostHandle(request, response, mv);
// ===== 第6步:视图渲染 =====
if (mv != null && mv.hasView()) {
render(mv, request, response);
}
// ===== 第7步:执行拦截器afterCompletion =====
chain.triggerAfterCompletion(request, response, null);
}
流程图:
用户请求 → HandlerMapping(查找Handler) → HandlerAdapter(执行Handler)
↓
HandlerExecutionChain(Handler + Interceptors)
↓
拦截器.preHandle() → Controller方法 → 拦截器.postHandle()
↓
ViewResolver(解析视图) → View(渲染页面)
↓
拦截器.afterCompletion() → 返回响应
关键点:
- HandlerMapping只负责查找Handler
- HandlerAdapter负责执行Handler
- 它们之间通过Handler对象关联
- HandlerAdapter使用参数解析器和返回值处理器
面试题4:SpringMVC如何实现参数绑定?
点击查看答案
答案:
参数绑定是通过HandlerMethodArgumentResolver(参数解析器)实现的。
核心流程:
java
/**
* 参数绑定流程
*/
public Object[] resolveArguments(HandlerMethod handlerMethod,
HttpServletRequest request) {
// 1. 获取方法的所有参数
MethodParameter[] parameters = handlerMethod.getMethodParameters();
Object[] args = new Object[parameters.length];
// 2. 遍历每个参数
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 3. 查找能处理该参数的解析器
HandlerMethodArgumentResolver resolver =
findArgumentResolver(parameter);
// 4. 解析参数值
args[i] = resolver.resolveArgument(parameter, request, response);
}
return args;
}
常见的参数解析器:
| 注解/类型 | 解析器 | 说明 |
|---|---|---|
@RequestParam |
RequestParamMethodArgumentResolver | 从request.getParameter()获取 |
@PathVariable |
PathVariableMethodArgumentResolver | 从URL路径中提取 |
@RequestBody |
RequestBodyMethodArgumentResolver | 从请求体解析JSON |
@RequestHeader |
RequestHeaderMethodArgumentResolver | 从请求头获取 |
@CookieValue |
CookieValueMethodArgumentResolver | 从Cookie获取 |
Model/ModelMap |
ModelMethodArgumentResolver | 提供Model对象 |
HttpServletRequest |
ServletRequestMethodArgumentResolver | 提供Servlet API对象 |
| 普通Java对象 | ServletModelAttributeMethodProcessor | 自动绑定请求参数到对象 |
示例:
java
@PostMapping("/save")
public String saveUser(
@RequestParam("name") String name, // 解析器1
@PathVariable Long id, // 解析器2
@RequestBody User user, // 解析器3
@RequestHeader("User-Agent") String ua, // 解析器4
Model model, // 解析器5
HttpServletRequest request) { // 解析器6
// 每个参数由不同的解析器处理
return "success";
}
参数解析器接口:
java
public interface HandlerMethodArgumentResolver {
// 是否支持该参数
boolean supportsParameter(MethodParameter parameter);
// 解析参数值
Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory);
}
面试题5:@RequestMapping的映射规则是什么?
点击查看答案
答案:
@RequestMapping支持多种映射规则:
1. URL路径映射
java
// 精确匹配
@RequestMapping("/user/list") // 只匹配/user/list
// 路径变量
@RequestMapping("/user/{id}") // 匹配/user/123,提取id=123
// Ant风格通配符
@RequestMapping("/user/*") // 匹配/user/abc,不匹配/user/a/b
@RequestMapping("/user/**") // 匹配/user下所有路径
// 正则表达式
@RequestMapping("/user/{id:\\d+}") // id必须是数字
2. HTTP请求方法
java
@RequestMapping(value = "/user", method = RequestMethod.GET)
@GetMapping("/user") // 等价于上面,只接受GET
@PostMapping("/user") // 只接受POST
@PutMapping("/user") // 只接受PUT
@DeleteMapping("/user") // 只接受DELETE
3. 请求参数条件
java
// 必须有name参数
@RequestMapping(value = "/user", params = "name")
// name参数值必须为admin
@RequestMapping(value = "/user", params = "name=admin")
// 必须有name和age参数
@RequestMapping(value = "/user", params = {"name", "age"})
// 不能有name参数
@RequestMapping(value = "/user", params = "!name")
4. 请求头条件
java
// 必须有Content-Type头
@RequestMapping(value = "/user", headers = "Content-Type")
// Content-Type必须为application/json
@RequestMapping(value = "/user",
headers = "Content-Type=application/json")
5. 请求体类型(consumes)
java
// 只接受JSON请求
@RequestMapping(value = "/user",
consumes = "application/json")
// 只接受表单请求
@RequestMapping(value = "/user",
consumes = "application/x-www-form-urlencoded")
6. 响应体类型(produces)
java
// 返回JSON
@RequestMapping(value = "/user",
produces = "application/json")
// 返回XML
@RequestMapping(value = "/user",
produces = "application/xml")
组合使用:
java
@RequestMapping(
value = "/user/{id}", // URL + 路径变量
method = RequestMethod.POST, // POST请求
params = "type=vip", // 必须有type=vip参数
headers = "Content-Type=application/json", // 请求头
consumes = "application/json", // 接受JSON
produces = "application/json" // 返回JSON
)
public User saveVipUser(@PathVariable Long id,
@RequestBody User user) {
// 只有满足所有条件才会匹配到这个方法
return userService.save(user);
}
面试题6:如何自定义HandlerMapping或HandlerAdapter?
点击查看答案
答案:
自定义HandlerMapping:
java
/**
* 自定义HandlerMapping示例
* 实现基于请求头的路由
*/
public class HeaderBasedHandlerMapping
extends AbstractHandlerMapping {
// 存储映射关系
private Map<String, Object> handlerMap = new HashMap<>();
/**
* 初始化映射
*/
public void initHandlerMap() {
handlerMap.put("mobile", new MobileController());
handlerMap.put("desktop", new DesktopController());
}
/**
* 查找Handler
*/
@Override
protected Object getHandlerInternal(HttpServletRequest request) {
// 从请求头获取客户端类型
String clientType = request.getHeader("Client-Type");
if (clientType == null) {
clientType = "desktop"; // 默认桌面端
}
// 根据客户端类型返回不同的Handler
return handlerMap.get(clientType);
}
}
/**
* 注册自定义HandlerMapping
*/
@Configuration
public class WebMvcConfig {
@Bean
public HeaderBasedHandlerMapping headerBasedHandlerMapping() {
HeaderBasedHandlerMapping mapping =
new HeaderBasedHandlerMapping();
mapping.initHandlerMap();
mapping.setOrder(0); // 设置优先级(数字越小越高)
return mapping;
}
}
自定义HandlerAdapter:
java
/**
* 自定义HandlerAdapter示例
* 处理自定义的Handler接口
*/
public interface MyHandler {
String process(HttpServletRequest request);
}
public class MyHandlerAdapter implements HandlerAdapter {
/**
* 判断是否支持该Handler
*/
@Override
public boolean supports(Object handler) {
return handler instanceof MyHandler;
}
/**
* 执行Handler
*/
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
MyHandler myHandler = (MyHandler) handler;
// 1. 执行Handler
String viewName = myHandler.process(request);
// 2. 创建ModelAndView
ModelAndView mv = new ModelAndView();
mv.setViewName(viewName);
return mv;
}
@Override
public long getLastModified(HttpServletRequest request,
Object handler) {
return -1;
}
}
/**
* 注册自定义HandlerAdapter
*/
@Configuration
public class WebMvcConfig {
@Bean
public MyHandlerAdapter myHandlerAdapter() {
return new MyHandlerAdapter();
}
}
使用自定义Handler:
java
@Component
public class CustomHandler implements MyHandler {
@Override
public String process(HttpServletRequest request) {
String action = request.getParameter("action");
if ("list".equals(action)) {
return "userList";
} else if ("detail".equals(action)) {
return "userDetail";
}
return "index";
}
}
面试题7:SpringMVC如何支持RESTful风格的URL?
点击查看答案
答案:
SpringMVC通过以下机制支持RESTful风格:
1. 路径变量(@PathVariable)
java
@RestController
@RequestMapping("/api/users")
public class UserRestController {
// GET /api/users/123
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
// GET /api/users/123/orders/456
@GetMapping("/{userId}/orders/{orderId}")
public Order getOrder(@PathVariable Long userId,
@PathVariable Long orderId) {
return orderService.findByUserAndId(userId, orderId);
}
}
2. HTTP方法映射
java
@RestController
@RequestMapping("/api/users")
public class UserRestController {
// GET /api/users - 查询所有
@GetMapping
public List<User> getAllUsers() { }
// GET /api/users/123 - 查询单个
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) { }
// POST /api/users - 创建
@PostMapping
public User createUser(@RequestBody User user) { }
// PUT /api/users/123 - 更新
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id,
@RequestBody User user) { }
// DELETE /api/users/123 - 删除
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) { }
}
3. @RequestBody和@ResponseBody
java
// 接收JSON请求体
@PostMapping
public User createUser(@RequestBody User user) {
// user对象自动从JSON反序列化
return userService.save(user);
}
// 返回JSON响应
@GetMapping("/{id}")
@ResponseBody // 或使用@RestController
public User getUser(@PathVariable Long id) {
// 返回的User对象自动序列化为JSON
return userService.findById(id);
}
4. @RestController注解
java
// @RestController = @Controller + @ResponseBody
@RestController // 所有方法默认返回JSON
@RequestMapping("/api/products")
public class ProductRestController {
@GetMapping
public List<Product> getAllProducts() {
// 自动转换为JSON数组
return productService.findAll();
}
}
5. 内容协商(Content Negotiation)
java
@GetMapping(produces = {"application/json", "application/xml"})
public User getUser(@PathVariable Long id) {
// 根据Accept请求头返回不同格式
// Accept: application/json -> 返回JSON
// Accept: application/xml -> 返回XML
return userService.findById(id);
}
6. HTTP状态码
java
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // 201 Created
public User createUser(@RequestBody User user) {
return userService.save(user);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT) // 204 No Content
public void deleteUser(@PathVariable Long id) {
userService.deleteById(id);
}
// 或使用ResponseEntity
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = userService.save(user);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(saved);
}
RESTful URL设计原则:
- 使用名词表示资源:
/users而不是/getUsers - 使用HTTP方法表示操作:GET查询、POST创建、PUT更新、DELETE删除
- 使用层级表示关系:
/users/123/orders表示用户123的订单 - 使用查询参数过滤:
/users?role=admin&status=active
五、📝 总结
核心知识点回顾
HandlerMapping(处理器映射器):
- 作用:根据URL查找Handler
- 核心方法 :
getHandler(HttpServletRequest request) - 常用实现:RequestMappingHandlerMapping、BeanNameUrlHandlerMapping
- URL匹配:支持精确匹配、路径变量、Ant通配符、正则表达式
HandlerAdapter(处理器适配器):
- 作用:执行Handler并返回ModelAndView
- 核心方法 :
supports(Object handler)、handle(...) - 常用实现:RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter
- 参数绑定:通过ArgumentResolver解析方法参数
- 返回值处理:通过ReturnValueHandler处理返回值
协作流程:
请求 → HandlerMapping查找 → HandlerAdapter执行 → 返回ModelAndView
扩展点:
- 自定义参数解析器(ArgumentResolver)
- 自定义返回值处理器(ReturnValueHandler)
- 自定义HandlerMapping
- 自定义HandlerAdapter
学习建议
- 理解设计模式:HandlerMapping使用策略模式,HandlerAdapter使用适配器模式
- 源码学习:阅读RequestMappingHandlerMapping和RequestMappingHandlerAdapter源码
- 实战练习:自定义参数解析器和返回值处理器
- 面试准备:重点掌握协作流程和扩展机制
🎉 恭喜你完成学习!
现在你已经深入理解了HandlerMapping和HandlerAdapter!
💪 继续实践,不断精进!
📧 有问题欢迎交流 | ⭐ 觉得有用请点赞分享