Spring MVC 参数绑定 、拦截器(Interceptor) 和 文件上传

一、参数绑定(Parameter Binding)

🎯 目标

Spring MVC 支持多种方式绑定请求参数到 Controller 方法的参数中,例如:

@GetMapping("/user")
public String getUser(@RequestParam("id") int id, @RequestBody User user, @PathVariable String name) {
    ...
}

这些注解的背后是通过 HandlerMethodArgumentResolver 实现的。


🔧 核心组件

注解对应 Resolver
@RequestParamRequestParamMethodArgumentResolver
@PathVariablePathVariableMethodArgumentResolver
@RequestBodyRequestResponseBodyMethodProcessor
@ModelAttributeModelAttributeMethodProcessor
@RequestHeaderRequestHeaderMethodArgumentResolver
HttpServletRequest, HttpServletResponseServletRequestMethodArgumentResolver

📦 参数解析流程

  1. Controller 方法注册时

    • RequestMappingHandlerMapping 会为每个方法创建一个 HandlerMethod
    • 所有参数信息都封装在 MethodParameter 对象中。
  2. 请求处理时

    • RequestMappingHandlerAdapterhandleInternal() 方法中:
      invocableMethod.invokeForRequest(webRequest, mavContainer, providedArgs);
      
  3. 调用链触发参数解析

    • ServletInvocableHandlerMethod.invokeForRequest() 内部调用:
      Object[] args = getMethodArgumentValues(webRequest, mavContainer, providedArgs);
      
  4. 遍历所有 ArgumentResolver 尝试匹配并解析参数

    • 每个 HandlerMethodArgumentResolver 都会判断是否支持当前参数类型:
      if (resolver.supportsParameter(parameter)) {
          return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
      }
      

🧪 @RequestParam 的解析过程

public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestParam.class);
    }

    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        // 从 request.getParameter() 获取值
        return request.getParameter(name);
    }
}

二、拦截器(Interceptor)

🎯 目标

拦截器用于在 Controller 方法执行前后做一些统一处理,如权限验证、日志记录等。

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 请求前处理
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    // Controller 执行后,视图渲染前处理
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    // 整个请求完成后的清理工作
}

📦 核心接口与类

  • 接口:HandlerInterceptor
  • 类:HandlerExecutionChain(持有多个 Interceptor)
  • 配置类:WebMvcConfigurer(重写 addInterceptors()

🧭 调用流程

  1. 获取 HandlerExecutionChain

    • DispatcherServlet.getHandler() 返回包含拦截器链的对象。
  2. preHandle 拦截器调用

    mappedHandler.applyPreHandle(request, response);
    
    • 按顺序调用每个 Interceptor 的 preHandle(),如果返回 false 则中断请求。
  3. Controller 执行完成后调用 postHandle

    mappedHandler.applyPostHandle(processedRequest, response, mv);
    
  4. afterCompletion 在整个请求结束后调用

    processDispatchResult(...);
    

🧪 自定义拦截器

@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getSession().getAttribute("user") == null) {
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }
}

注册方式(配置类):

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/css/**");
    }
}

三、文件上传(Multipart 解析)

🎯 目标

处理客户端上传的文件,使用 MultipartResolver 接口解析 multipart/form-data 请求体。


📦 核心类

  • 接口:MultipartResolver
  • 实现类:StandardServletMultipartResolver(基于 Servlet 3.0+)、CommonsMultipartResolver(Apache Commons FileUpload)

🧭 文件上传处理流程

  1. 请求进入 DispatcherServlet.doDispatch()

  2. 检查是否为 multipart 请求

    if (multipartResolver != null && multipartResolver.isMultipart(request)) {
        processedRequest = multipartResolver.resolveMultipart(request);
    }
    
  3. resolveMultipart() 方法解析请求内容

    • StandardServletMultipartResolver 使用 request.getParts() 获取所有 part。
    • 文件部分会被封装成 MultipartFile
  4. Controller 中接收 MultipartFile

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        ...
    }
    
  5. 参数解析器自动识别 MultipartFile

    • RequestParamMethodArgumentResolver 会检测到参数类型为 MultipartFile 并正确注入。

🧪 文件上传 Controller

@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
    if (!file.isEmpty()) {
        // 处理文件
        String originalFilename = file.getOriginalFilename();
        // 保存文件逻辑...
    }
    return "redirect:/success";
}

⚙️ 配置 MultipartResolver(可选)

在 Java 配置中启用 Multipart 支持:

@Bean
public MultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
}

同时需要设置 spring.servlet.multipart.enabled=true(Spring Boot)或配置 MultipartConfigElement


✅ 总结

功能组件/类作用
参数绑定HandlerMethodArgumentResolver自动解析请求参数并注入到 Controller 方法中
拦截器HandlerInterceptor控制请求生命周期中的前置、后置处理
文件上传MultipartResolver解析 multipart 请求,生成 MultipartFile 对象

📚 源码路径

可以查看以下类的源码来进一步理解上述功能:

功能源码路径
参数绑定org.springframework.web.method.support.HandlerMethodArgumentResolver
拦截器org.springframework.web.servlet.HandlerInterceptor
文件上传org.springframework.web.multipart.MultipartResolver
DispatcherServletorg.springframework.web.servlet.DispatcherServlet
HandlerExecutionChainorg.springframework.web.servlet.HandlerExecutionChain

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
OSZAR »