导读:本期聚焦于唐僧创作的《Spring Boot 如何通过实现 WebMvcConfigurer 接口自定义 MVC 配置与拦截器?》,敬请观看详情。Spring Boot 默认提供的 MVC 配置已经能满足大部分场景,但遇到接口路径放行、登录校验、跨域处理、参数格式化这类定制需求时,就需要动手扩展了。WebMvcConfigurer 接口是 Spring 官方推荐的扩展入口,通过实现它的各个回调方法,可以在不覆盖默认配置的前提下灵活定制拦截器、跨域策略、消息转换器、静态资源映射等内容。本文将从接口的基本设计讲起,详细演示自定义 HandlerInterceptor 拦截器的完整流程,包括拦截器注册、路径匹配规则、多拦截器执行顺序控制,并顺带介绍 addResourceHandlers、addCorsMappings、addFormatters 等常用回调的用法与避坑要点,帮你把 Spring MVC 的定制能力真正用起来。

Spring Boot 的自动配置让 Web 应用开箱即用,但真实业务里几乎一定会遇到需要定制 Spring MVC 行为的场景:比如给所有后台接口加一个登录校验的拦截器、放开某些静态资源路径、统一处理跨域请求,或者注册自定义的日期格式化器。在老版本 Spring 中,大家习惯继承 WebMvcConfigurerAdapter 或者直接继承 WebMvcConfigurationSupport,而现在的推荐做法非常简单——直接实现 WebMvcConfigurer 接口。这篇文章就来把这个接口的常用扩展点讲透,重点演示拦截器的完整实现流程。

Spring Boot 如何通过实现 WebMvcConfigurer 接口自定义 MVC 配置与拦截器?

一、为什么是 WebMvcConfigurer 而不是 WebMvcConfigurationSupport

先厘清一个概念。Spring Boot 关于 MVC 的自动配置类是 WebMvcAutoConfiguration,它的头部有一个条件注解 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),意思是:只要容器中出现了 WebMvcConfigurationSupport 类型的 Bean,自动配置就整体失效。所以如果你为了加一个拦截器去继承 WebMvcConfigurationSupport,代价是把 Spring Boot 默认提供的静态资源映射、Jackson 消息转换器、路径匹配等全部配置都丢掉了,得不偿失。

WebMvcConfigurer 的定位则完全不同,它是一个纯回调接口,Spring Boot 在执行自动配置时会收集容器中所有 WebMvcConfigurer 类型的 Bean,逐个调用其中的方法来做增量式定制。用一句话概括:WebMvcConfigurationSupport 是全量接管,WebMvcConfigurer 是锦上添花。除非你确实需要彻底重写 MVC 行为,否则永远优先选择后者。

另外提一下历史包袱:Spring 5 之前由于 Java 8 之前的接口不能有默认方法,所以官方提供了 WebMvcConfigurerAdapter 抽象类作为过渡。Spring 5 之后接口方法都有了 default 实现,这个 Adapter 已经被标记为废弃,新项目里不要再用了,直接实现接口、只覆写需要的方法即可。

二、实现 WebMvcConfigurer 定制基础配置

先看一个基础骨架。定义一个配置类,标注 @Configuration 并实现 WebMvcConfigurer,之后想定制什么就覆写对应的方法:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    // 定制跨域策略
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }

    // 定制静态资源映射
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/files/**")
                .addResourceLocations("file:D:/upload/");
    }

    // 注册自定义格式化器
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
    }
}

这段代码里覆写了三个常用方法。addCorsMappings 处理跨域,注意如果要携带 Cookie(allowCredentials 为 true),不能再用 allowedOrigins("*"),必须改用 allowedOriginPatterns,否则启动时会直接抛异常,这是很多人踩过的坑。

addResourceHandlers 常用于把本地磁盘目录映射成可访问的 URL。写本地路径时注意前缀必须是 file:,Windows 下路径形如 file:D:/upload/,结尾的斜杠建议保留,表示按目录解析。如果只是想把静态资源放到 classpath 下的自定义目录,则用 classpath:/static/xxx/ 这样的写法。

addFormatters 允许注册全局的类型转换器,比如上面注册的日期格式化器,可以让前端传 2024-05-01 这种字符串时自动绑定到 Date 类型的参数上,省去了在每个接口里手动解析的麻烦。类似的还有 configureMessageConverters 可以替换或追加 HttpMessageConverter,configurePathMatch 可以调整路径匹配策略,按需选用即可。

三、自定义拦截器并注册到拦截链

拦截器是 WebMvcConfigurer 使用频率最高的场景。完整流程分两步:先写拦截器,再注册。拦截器本身实现 HandlerInterceptor 接口,它有三个可覆写的方法:preHandle 在控制器方法执行前调用,返回 false 会中断后续流程;postHandle 在控制器执行后、视图渲染前调用;afterCompletion 在整个请求结束后调用,适合做资源清理和耗时统计。

下面写一个典型的登录校验拦截器,从请求头取 token 并校验:

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null || token.isEmpty()) {
            response.setStatus(401);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}");
            return false; // 中断请求,不再进入控制器
        }
        // 校验通过后可以把用户信息放进请求属性,供后续使用
        request.setAttribute("userId", parseUserId(token));
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception ex) {
        long cost = System.currentTimeMillis()
                - (Long) request.getAttribute("startTime");
        System.out.println("接口耗时:" + cost + "ms");
    }
}

有了拦截器类,接着在配置类中通过 addInterceptors 方法把它注册进拦截链:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/api/**")       // 拦截的路径
                .excludePathPatterns("/api/login", "/api/register"); // 放行的路径
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns("/**")
                .order(1); // 数值越小优先级越高
    }
}

注册时有几个细节值得注意。第一,路径规则是 Ant 风格通配符,/** 匹配多层路径,/* 只匹配一层,别混淆。第二,excludePathPatterns 的优先级高于 addPathPatterns,被排除的路径一定不会进拦截器,所以登录、注册这类匿名接口要在这里显式放行,否则用户永远登不上去。第三,如果多个拦截器都命中同一路径,执行顺序默认按注册顺序,也可以用 order 方法显式指定,数值小的先执行。

多拦截器下 preHandle 按顺序正向执行,postHandle 和 afterCompletion 则按逆序执行,形成一个可预测的洋葱模型。如果某个 preHandle 返回了 false,前面已通过的所有拦截器的 afterCompletion 依然会被调用,这一点对于释放资源很重要,可以放心把清理逻辑写在 afterCompletion 里。

四、拦截器的常见坑与替代方案

实际使用中有几个容易出问题的地方。一是拦截器无法直接注入到没有注册为 Bean 的场景,如果拦截器里需要用到 Service,建议把拦截器本身声明为 Bean,通过构造器注入依赖,然后在 addInterceptors 里传入对象而非 new 出来的实例:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final LoginInterceptor loginInterceptor;

    public WebConfig(LoginInterceptor loginInterceptor) {
        this.loginInterceptor = loginInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**");
    }
}

@Component
public class LoginInterceptor implements HandlerInterceptor { ... }

二是拦截器与 Filter 的选择问题。Filter 是 Servlet 规范层的组件,在拦截器之前执行,能拿到原始的请求和响应,适合字符编码、全局日志这类与业务无关的通用处理;拦截器处于 Spring MVC 上下文中,能拿到对应的 HandlerMethod,知道请求最终会打到哪个控制器的哪个方法,适合做权限校验、登录检查这类业务级拦截。两者不是互斥关系,按层次各司其职即可。

三是异步请求的处理。如果接口返回 DeferredResult 或使用 SSE,postHandle 可能不会按预期执行,此时应关注 AsyncHandlerInterceptor 的 afterConcurrentHandlingStarted 回调,避免在异步场景下统计或清理逻辑失效。

整体来看,WebMvcConfigurer 提供的是一套声明式的 MVC 定制入口,理解了它增量扩展的设计思路,再掌握 addInterceptors、addCorsMappings、addResourceHandlers 这几个高频方法,日常开发中百分之九十的 MVC 定制需求都能优雅地解决,而且完全不会破坏 Spring Boot 的自动配置红利。

WebMvcConfigurerSpring Boot拦截器MVC配置修改时间:2026-09-03 01:46:55

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49270.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。