HandlerMapping和HandlerAdapter完全指南

掌握核心组件 | 💡 理解映射原理 | 🔥 面试必备知识


📖 前言

💭 「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(处理器)。

核心作用:

  1. URL映射:建立URL和Controller方法的映射关系
  2. 查找Handler:根据请求URL查找对应的处理器
  3. 返回执行链:返回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
}

核心功能:

  1. 参数解析:从HTTP请求中提取参数,绑定到方法参数
  2. 方法调用:反射调用Controller方法
  3. 返回值处理:将返回值转换为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() → 返回响应

关键点:

  1. HandlerMapping只负责查找Handler
  2. HandlerAdapter负责执行Handler
  3. 它们之间通过Handler对象关联
  4. 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

学习建议

  1. 理解设计模式:HandlerMapping使用策略模式,HandlerAdapter使用适配器模式
  2. 源码学习:阅读RequestMappingHandlerMapping和RequestMappingHandlerAdapter源码
  3. 实战练习:自定义参数解析器和返回值处理器
  4. 面试准备:重点掌握协作流程和扩展机制

🎉 恭喜你完成学习!

现在你已经深入理解了HandlerMapping和HandlerAdapter!

💪 继续实践,不断精进!

📧 有问题欢迎交流 | ⭐ 觉得有用请点赞分享

相关推荐
弹简特2 小时前
【JavaEE10-后端部分】SpringMVC05-综合案例1-从加法计算器看前后端交互:接口文档与HTTP通信详解
java·spring boot·spring·http
躲在云朵里`2 小时前
同一账号在同一客户端类型只能登录一次
前端·spring·bootstrap
啦啦啦_99992 小时前
SpringAI Alibaba(SAA) 之 SSE
spring
弹简特3 小时前
【JavaEE12-后端部分】SpringMVC07-综合案例3-从留言板看前后端交互:接口文档与HTTP通信详解
spring boot·网络协议·spring·http·java-ee·交互
minh_coo3 小时前
Spring单元测试之反射利器:ReflectionTestUtils
java·后端·spring·单元测试·intellij-idea
弹简特3 小时前
【JavaEE11-后端部分】SpringMVC06-综合案例2-从用户登录看前后端交互:接口文档与HTTP通信详解
java·spring boot·spring·http·java-ee·tomcat
xing-xing4 小时前
Spring Data Elasticsearch
后端·spring·elasticsearch
xing-xing4 小时前
Spring Data项目
数据库·spring
vx_Biye_Design6 小时前
【关注可免费领取源码】云计算及其应用网络教学系统--毕设附源码35183
java·spring·spring cloud·servlet·eclipse·云计算·课程设计