ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SpringMVC核心原理与实战:Controller、拦截器过滤器避坑指南

SpringMVC核心原理与实战:Controller、拦截器过滤器避坑指南 开头做Java后端的朋友应该都对SpringMVC不陌生。它是Spring家族里专门负责Web层的那块拼图从最早的XML配置到处处注解的Spring Boot时代它的核心地位几乎没有动摇过。不管你是刚接触Java Web的萌新还是写过几年接口的熟练工SpringMVC这套东西都是绕不开的坎。这篇博文不是把官方文档抄一遍而是把我这几年实打实用过、踩过、研究过的SpringMVC关键脉络重新梳理一遍重点放在Controller的用法细节、拦截器和过滤器的使用场景与区别以及日常排查问题的那点经验上。我见过不少同事接口能写通但一问到DispatcherServlet做了啥、Interceptor和Filter到底什么关系、为什么请求进不了Controller就支支吾吾说不上来。其实这些东西搞清楚之后你在团队里定位问题的速度会快很多写代码的时候心里也有底。这篇尽量讲得直白一点把原理和实操结合起来适合正在学SpringMVC的人当复习提纲也适合工作一两年的同学查漏补缺。1. SpringMVC的整体设计与运行机制1.1 Servlet时代到SpringMVC的演进要说SpringMVC得先聊聊它在整个Java Web里的位置。早期的Servlet开发每个请求都对应一个Servlet类你想处理一个用户列表的请求要写一个UserListServlet处理用户详情又要写一个UserDetailServlet每一个都要在web.xml里配置映射。这种模型最大的问题是类爆炸而且每个Servlet里拿到请求参数、调业务、转发视图这些样板代码都一模一样纯属体力活。SpringMVC的出现就是为了解决这块的重复劳动。它把Web层的职责做了拆分前端控制器负责统一接收请求处理器映射器负责找到处理某个请求的Controller方法处理器适配器负责真正调用这个Controller方法视图解析器负责把逻辑视图名解析成真正的页面。打一个不太严谨但容易理解的比方DispatcherServlet就像饭店前台的迎宾员你来了它帮你带到对应的餐桌Controller至于后厨怎么做菜、端菜的服务员是谁它不直接插手只负责调度。核心思想就是单一入口加组件分工。一个Web应用的所有请求先进DispatcherServlet然后由它分发到各个Controller整个过程对开发者来说都是透明的你只需要关注自己的Controller方法怎么写、参数怎么接、返回值怎么给剩下的事情交给框架去协调。1.2 一次请求的完整生命周期理解了整体设计我们再看一次请求从进入到返回在SpringMVC里到底走了哪些环节。假设你在浏览器里输入了一个/user/detail?id18的地址请求先到DispatcherServlet它会拿着这个URL去问HandlerMapping有没有哪个Controller方法对应上了这个地址。HandlerMapping收到URL之后会去匹配所有注册好的HandlerMethod映射找到返回一个执行链HandlerExecutionChain这个执行链里不光有Controller方法还挂了应该在该处理器上执行的拦截器列表。DispatcherServlet拿到执行链再去选择一个合适的HandlerAdapter。不同版本的SpringMVC适配器策略不一样现在最常用的RequestMappingHandlerAdapter就是专门服务基于注解的Controller的。它会解析方法的参数、校验、执行方法本身然后把返回值封装成ModelAndView。如果是前后端分离的接口Controller方法上面标了ResponseBody返回值就会被HttpMessageConverter序列化成JSON直接写回响应。如果返回值是视图名DispatcherServlet就会把ModelAndView交给ViewResolver去解析最终渲染出HTML页面返回给浏览器。在这个流程里DispatcherServlet是整个链路的绝对中枢它把这些组件全部串联起来开发者通过实现各种接口就能在特定环节插入自己的逻辑。理解这个生命周期之后后面看拦截器、过滤器到底作用在哪一环就一目了然了。1.3 三种主流配置方式对比SpringMVC的配置方式经历了三个明显的阶段很多老项目和新项目并存最好都看得懂。最早是纯XML配置要在web.xml里配DispatcherServlet再写一个springmvc.xml配置包扫描、视图解析器注解驱动要加一句mvc:annotation-driven /。这种方式配置文件又多又长一个SpringMVC的配置动辄上百行但它的好处是显式所有配置都看得见摸得着。到了Spring 3.0之后官方逐步推荐用Java配置类来替代XML写一个类实现WebMvcConfigurer加上EnableWebMvc注解以前XML里配的东西现在都变成了Java方法。到了Spring Boot时代就更简单了只要引入spring-boot-starter-webSpringMVC基本是零配置就能跑起来的默认内嵌Tomcat默认开启注解驱动你只管写Controller就行。我个人的建议是如果只是做新项目直接用Spring Boot的默认SpringMVC配置就好没必要手动去改什么。但遇到老项目迁移、特殊定制需求你得会看XML配置、知道WebMvcConfigurer每个回调方法对应的是以前的哪个配置项。三种配置方式的演进本质是同一个框架在不同时期的呈现形态底层组件一模一样。2. Controller层的深度使用详解2.1 Controller与RestController的取舍Controller是SpringMVC里开发天天打交道的核心。先说最基本的注解选择Controller和RestController只差一个ResponseBody但用的时候很容易搞混。类上加RestController就等于每个方法默认带上了ResponseBody方法返回值会直接序列化成JSON或字符串写进响应体不会走视图解析器。典型场景是纯粹的API接口开发比如后端给前端App提供数据。如果类上是Controller方法返回的字符串会被当作逻辑视图名去解析适合传统的服务端渲染场景比如用Thymeleaf、FreeMarker做页面。有人可能说我前后端分离不用RestController行不行可以但要在每个方法上手动加ResponseBody麻烦且容易漏掉没有什么意义。实际开发里我还见过一种场景同一个Controller里既有返回JSON的接口又有跳页面的入口方法。这时候建议拆成两个类或者明确区分开不要混在一个类里不然维护的人看着头疼。原则上是接口类统一用RestController页面跳转类用Controller。2.2 核心注解的匹配规则与使用场景RequestMapping及它衍生出来的GetMapping、PostMapping、PutMapping、DeleteMapping是Controller方法的入口标识。这些注解看着简单但有一些细节值得注意。路径匹配的规则上/user/{id}这种方式叫路径变量一个花括号就是一个参数位配合PathVariable使用。还有一个容易踩坑的规则是/user、/user/这两个路径在SpringMVC里默认是有区别的结尾斜杠可能导致404虽然有的版本做了匹配优化但你在写接口路径时尽量统一规范不要一会儿带斜杠一会儿不带。RequestMapping还有一个容易被忽略的produces和consumes属性。produces可以指定接口返回的内容类型比如application/json;charsetUTF-8如果请求头里的Accept不匹配请求压根不会进这个方法。consumes则用来限定Content-Type。这两个属性在处理同URL不同响应格式时特别有用比如一个方法返回HTML一个方法返回JSON可以用produces区分开。这个技巧在对接外部系统时有奇效冲突少了代码也干净。2.3 参数绑定的几种姿势和坑点Controller方法接参数方式太多了最常见的用RequestParam、PathVariable、RequestBody三件套。这三个注解各有明确分工RequestParam绑定URL上的查询参数或表单字段PathVariable绑定URL路径里的变量RequestBody把请求体里的JSON或XML反序列化成对象。分开说细节。RequestParam不传的情况下required默认是true意思是请求里必须带这个参数不带就是400错误。很多联调场景前端漏传了某个字段后端直接报400排查半天才明白是这个原因。所以不确定前端会不会传的参数建议把required设为false或者用defaultValue给定一个默认值。RequestBody用起来舒服但有几个前置条件项目里要有Jackson依赖方法参数不能是String以外的基本类型直接接收除非你自己写了转换器还有POST请求的Content-Type得是application/json。很多人用Postman测接口没问题换了前端传值就变成415十有八九是Content-Type没设置对。日期格式的绑定也是高频问题。前端传2024-06-18 10:30:00这种字符串后端用LocalDateTime接收直接报400是常态。解决方式有几种在字段上加JsonFormat注解或者全局配置Jackson的日期序列化和反序列化格式。我建议项目里统一用全局配置避免每个字段都写注解。2.4 返回值处理的完整链路Controller方法的返回值怎么变成响应这里再展开讲一下。没有ResponseBody时返回String会被当作LogcalViewName走视图解析器返回ModelAndView是程序员手动控制模型和视图老项目里常见返回void则完全由Servlet自己往HttpServletResponse里写东西。有ResponseBody时返回对象包括List、Map、自定义VO会被交给HttpMessageConverter处理。Spring Boot默认配置了Jackson对象序列化成JSON。这里有个性能细节如果返回值里含有很多不想暴露给前端的字段比如密码、内部标识建议用DTO而不是直接丢Entity出去再配合JsonIgnore精细控制字段的序列化。直接返回Entity一时爽等前端催着要脱敏接口时就开始头疼了。接口统一返回结构也是后端团队需要尽早规定的状态码、消息、数据三个字段基本是标配。有的团队封装了Result类用泛型承载数据Controller方法签名清晰前端处理也统一。我建议团队内定一个规范别各自返回各自的结构不然联调和排错成本成倍上涨。3. 拦截器Interceptor的原理与实战3.1 拦截器三大方法的执行时机拦截器也就是Interceptor是SpringMVC框架层面的一个扩展点。先记住最重要的一句话拦截器只拦截经过DispatcherServlet分发的Controller请求不拦截静态资源的访问需要另外配置也不参与Servlet容器的Filter链。实现一个拦截器核心是实现HandlerInterceptor接口。这个接口有三个默认方法从Spring 5后都变成了default方法按需重写即可preHandle在Controller方法执行之前调用。返回值是boolean返回true则放行继续执行返回false则中断请求后续的postHandle、afterCompletion都不会执行。这是做鉴权、权限校验、日志记录的好地方。postHandle在Controller方法执行之后、视图渲染之前被调用。此时ModelAndView已经生成可以往Model里补数据或者对视图做二次处理。但前后端分离的项目里这个方法用得不多因为JSON响应已经写入了。afterCompletion整个请求处理完成后回调不管有没有异常都会执行。适合做资源清理、记录完整的调用日志、计算吞吐量。三个方法的执行顺序在单个拦截器场景下是固定的preHandle最先postHandle次之afterCompletion最后。多个拦截器并存时执行顺序就要看它们的注册顺序了这个后面细说。3.2 拦截器的注册与路径匹配细节写好了拦截器类还要注册到SpringMVC才能生效。在Spring Boot中注册方式是实现WebMvcConfigurer接口重写addInterceptors方法通过InterceptorRegistry往里面add。注册时最需要留意的是路径匹配规则。两个常见写法/**代表匹配所有路径包括多级子路径/*只匹配一级路径比如/api/test能匹配但/api/test/abc就匹配不到。这个区别我见过不止一次写错配了个/*以为拦截了所有接口结果某个多级路径的接口绕过了登录校验这在生产环境就是妥妥的安全事故。排除路径的配置也有讲究。像登录接口、注册接口、验证码接口、健康检查接口一般都要在拦截器里放行不然用户还没登录就被弹回去自己人调试都费劲。排除路径用的是excludePathPatterns方法被排除的路径不经过拦截器。还有拦截器的Bean生命周期注册的拦截器需要是Spring容器管理的Bean才能在内部注入Service、Mapper等依赖。如果用new方式直接创建拦截器实例通过Autowired注入的那些依赖全是null跑起来会空指针。这个坑在代码Review时经常能发现。3.3 一个登录鉴权拦截器的完整写法现在用最典型的登录校验场景串一下拦截器的完整写法。Component public class LoginInterceptor implements HandlerInterceptor { Autowired private UserTokenService userTokenService; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null userTokenService.validateToken(token)) { return true; } response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\message\:\未登录或登录已过期\}); return false; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { long startTime (Long) request.getAttribute(startTime); long cost System.currentTimeMillis() - startTime; System.out.println(request.getRequestURI() 耗时 cost ms); } }preHandle里第一次判断是放行OPTIONS预检请求这是前后端分离项目里绕不开的细节浏览器CORS机制会先发一个OPTIONS请求试探后端如果拦截器把这个请求也拦了前端控制台全是跨域错误其实根本不是跨域配置的问题就是拦截器太严格了。而耗时统计在afterCompletion里做起来特别顺手因为不管业务代码有没有抛异常它都会执行统计出来的时间基本是准确的。注意要手动把startTime放进request的attribute里在进入Controller之前开始计时。注册这个拦截器Configuration public class WebConfig implements WebMvcConfigurer { Autowired private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/login, /api/register, /api/captcha); } }3.4 多个拦截器的责任链顺序多个拦截器同时生效时执行的顺序是按注册顺序排列的。假设注册了拦截器A、拦截器B那么请求的执行顺序是A.preHandle、B.preHandle、Controller、B.postHandle、A.postHandle、B.afterCompletion、A.afterCompletion这个顺序很像洋葱模型的剥开和回卷。可能有人觉得奇怪postHandle和afterCompletion的顺序为什么是反的因为它本质是责任链模式的变体先进入的先处理后进入的后处理但是到了清理资源阶段后进入的反而要先完成。这个顺序在实际项目里的意义很大。比如你想做一个接口SDK的服务端希望先做签名校验再做登录鉴权再把这两个逻辑做成两个独立的拦截器注册顺序就是签名校验在前、鉴权在后。如果注册顺序反了未签名的请求先进了鉴权逻辑返回的错误信息对调用方来说就会变得莫名其妙。我个人的建议是一个团队里拦截器的数量不要贪多两三个足够实在太多可以考虑合并逻辑。拦截器顺序一旦乱掉排查成本极高因为每个拦截器都像一道暗门请求过不过完全看它脸色。4. 过滤器Filter的使用、配置与两者对比4.1 过滤器在Servlet规范中的定位过滤器Filter是Servlet规范里定义的东西它在SpringMVC出现之前就存在了。过滤器的本质是请求链路上的一个关卡浏览器发来的请求到Servlet容器之后会经过一系列Filter然后才到达ServletSpringMVC里就是DispatcherServlet。Filter能把请求和响应都拦截下来做处理然后决定是否放行到下一个环节。Filter和SpringMVC的Interceptor最大的区别在于所属规范和生效时机。Filter依赖Servlet容器不感知SpringMVC的存在Interceptor依赖SpringMVC框架必须在DispatcherServlet分发之后才能介入。所以Filter更适合做一些纯Servlet层面的横切逻辑比如请求编码设置、跨域配置、请求日志、XSS过滤、压缩响应Interceptor则适合和业务深度绑定的鉴权、权限校验、通用参数注入等操作。Spring Boot项目里注册Filter的方式也很多。可以在Filter实现类上加WebFilter注解再配合ServletComponentScan扫描也可以通过FilterRegistrationBean手动注册。我更推荐后者因为控制力更强、可以指定URL匹配模式、可以设置执行顺序。Component public class CustomRequestLogFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest (HttpServletRequest) request; long start System.currentTimeMillis(); chain.doFilter(request, response); long cost System.currentTimeMillis() - start; System.out.println(httpRequest.getRequestURI() cost cost ms); } }4.2 Spring Boot里注册Filter的两种正确姿势先展示基于FilterRegistrationBean的方式Configuration public class FilterConfig { Bean public FilterRegistrationBeanRequestLogFilter logFilter() { FilterRegistrationBeanRequestLogFilter registration new FilterRegistrationBean(); registration.setFilter(new RequestLogFilter()); registration.addUrlPatterns(/*); registration.setOrder(1); return registration; } Bean public FilterRegistrationBeanCorsFilter corsFilter() { FilterRegistrationBeanCorsFilter registration new FilterRegistrationBean(); registration.setFilter(new CorsFilter()); registration.addUrlPatterns(/*); registration.setOrder(0); return registration; } }setOrder方法设置的order数字越小优先级越高Filter执行顺序越靠前。这个顺序非常关键比如CORS跨域过滤器一定要排在编码过滤器之后、登录鉴权过滤器之前否则预检请求如果先被登录校验拦截跨域还是会失败。WebFilter注解的方式更懒人一点但它和过滤器顺序的控制不如FilterRegistrationBean灵活而且需要注意Spring Boot启动类的ServletComponentScan注解是否加了。两种方式的效果等价都可以把Filter纳入Spring容器管理从而可以注入其他Bean。不过我在几个项目里实测下来FilterRegistrationBean的可配置性和可读性都更好所以更推荐。4.3 过滤器与拦截器的执行顺序对比表很多初学者搞不清Filter和Interceptor到底谁先执行。我用一个比较直观的描述过滤器先执行拦截器后执行。它们嵌套的顺序大致是请求进入Servlet容器 → 经过Filter链 → 到达DispatcherServlet → 经过HandlerInterceptor链preHandle → 执行Controller方法 → 经过HandlerInterceptor链postHandle/afterCompletion → 返回响应经过Filter链 → 到达客户端下面这个表帮大家速记两者的区别对比项过滤器 Filter拦截器 Interceptor所属规范Servlet规范SpringMVC框架生效时机DispatcherServlet之前DispatcherServlet之后访问Spring Bean需要注册为Bean否则不行本身就是Spring管理的Bean使用场景编码、CORS、日志、XSS鉴权、权限、业务参数处理是否感知Controller不感知可访问HandlerMethod用一句话总结我的观点能放在Interceptor做的尽量别放Filter做因为Interceptor更贴近Spring生态能拿到具体的HandlerMethod信息处理业务更顺手Filter层只放那些与Servlet环境强相关的通用逻辑比如编码、跨域、日志。4.4 过滤器拦截器的混合使用案例一个比较典型的场景是这样系统需要跨域支持还需要登录鉴权还要统计每个接口耗时。实现时我会把跨域用Filter做把登录鉴权用Interceptor做把耗时统计拆成两部分——粗粒度的耗时用Filter做细粒度的Controller执行耗时用Interceptor的afterCompletion做。为什么会这样拆因为跨域是Servlet层面的问题和PHP、Node或者其他后端框架互通时都有类似逻辑放在Filter里恰好合适。登录鉴权需要拿token去数据库查用户这个动作需要用到Service层Interceptor正好在Spring容器里注入Service毫无障碍。耗时统计的两段分别回答两个问题接口整体耗时多少Filter层、Controller方法本身耗时多少Interceptor层。两个数据一对比就能快速定位耗时的瓶颈在框架层还是在业务代码层。这些经验不是在文档里读到的而是真实项目压测、排查问题时一点点磨出来的。5. 高频问题排查与避坑手册5.1 请求404的常见原因梳理SpringMVC项目里遇到404优先排查的次序我建议这样走一遍。最普遍的原因之一是包扫描路径不对。Spring IoC容器扫不到Controller类自然也就注册不了对应的映射。Spring Boot启动类默认扫描其所在包及子包如果Controller放在其他包下要手动加ComponentScan。这个错法很隐蔽项目启动时不报错一请求全404。另一个原因是当前端请求的路径和后端Controller映射的路径不一致。有时候前端多了一个context-path或者后端方法路径写错了一个标签排查时要先确认URL到底打到哪个路径再去看Controller的RequestMapping全路径。可以用Spring Boot的actuator指标查看已注册的映射这是排查利器。还有一种比较少见但容易误导人的情况同一个URL同时被两个Controller方法映射了SpringMVC启动时会直接报错提示ambiguous mapping但有时候只有环境差异才会触发比如测试环境有重复扫描生产环境没有。所以整个团队约定好接口路径唯一性规范很有必要。5.2 参数绑定失败的经典报错与修复参数绑定失败出现的报错一般是400配合日志里的MethodArgumentTypeMismatchException或者HttpMessageNotReadableException。这两个异常形态很典型前者是路径变量或请求参数的类型转换失败比如传了一个非数字字符串给Integer类型的参数后者是JSON反序列化失败比如日期格式不对、字段类型不匹配。排查步骤第一步先看前端实际发送的请求长什么样用浏览器开发者工具或Postman直接发同样的请求复现。第二步看后端日志的完整堆栈确定是哪个参数出了问题。第三步如果是日期格式要么统一约定前端传时间戳要么后端全局配置Jackson的日期格式让团队内所有系统都遵守同一套规范。还见过一个更隐蔽的坑Postman传一个JSON数组给RequestBody List 如果请求头Content-Type是text/plain后端一样报415或400。排查时记得检查Content-Type尤其是前端用原生axios/XMLHttpRequest时header没有正确设置就会这样。5.3 拦截器不生效的检查清单拦截器不生效大多数人第一反应是代码逻辑写错了但实际上配置层面出问题的概率更大。我总结了一个检查清单按顺序过一遍准能找到问题确认配置类上有Configuration注解没有这个注解WebMvcConfigurer的实现不会被Spring Boot加载。确认拦截器实例是被Spring管理的Bean如果拦截器内部有注入的依赖检查是否用了new的方式创建。检查addPathPatterns的匹配规则/*和/**的区别前面说过了别写错。确认excludePathPatterns没有把要拦截的路径误排除掉。确认项目里没有多个WebMvcConfigurer实现互相覆盖Spring Boot只会加载主配置类的addInterceptors方法。这个清单救过我不少次尤其是多个配置类冲突的情况。比如项目里有一个基础的WebConfig配置了拦截器另一个第三方SDK的自动配置也实现了WebMvcConfigurer两个配置类同时存在时不排除可能互相影响。5.4 静态资源访问与拦截器冲突的解决方案静态资源的访问问题也是SpringMVC项目的经典坑。引入Spring Security或自定义拦截器后css、js、图片这些静态文件可能全部无法访问或者被拦截器拦下来。Spring Boot默认会处理静态资源的映射classpath:/static/下的文件可以直接通过根路径访问。但是一旦你实现了WebMvcConfigurer并重写了addResourceHandlers默认的静态资源映射可能被覆盖需要手动补上Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/static/**) .addResourceLocations(classpath:/static/); }如果用了自定义拦截器并且拦截了/**排除了静态资源的路径也是必须的。比如排除/static/**、/favicon.ico、/error等路径。另外有个细节Spring Boot里如果配置了server.servlet.context-path静态资源的路径映射也会变前端引用的路径里面要带上这个前缀不然看起来像404实际上是路径没对。5.5 中文乱码问题的多层排查思路中文乱码在SpringMVC项目里从简单到复杂分为接、存、返三个阶段。接收参数乱码多半是编码过滤器没有配置或者配置位置不对。Spring Boot环境中spring.http.encoding可以配置编码但如果你自定义了Filter一定要注意执行顺序CharacterEncodingFilter必须放在最前面否则请求体在到达业务代码时已经被错误解码了。存储到数据库乱码那要看数据库连接串有没有带characterEncoding参数比如useUnicodetruecharacterEncodingutf8。很多项目在接口层返回JSON正常存库查出来是乱码问题基本都在JDBC连接串上。响应JSON乱码检查Controller或配置类的produces属性是否指定了charsetUTF-8或者在Jackson配置里关闭强制ASCII转义。我见过一个项目因为漏了配置前端拿到的JSON里中文全部变成了\u开头显示给用户就是乱码。定位方式很简单用Postman直接请求接口看响应头的Content-Type里有没有charsetUTF-8。5.6 过滤器注册顺序踩坑记录Filter的顺序问题我前面提了这里再展开说说具体的坑。有一次做跨域改造前端死活说跨域报错后端日志里也没看到请求进来。排查到最后发现CORS Filter的order设得比登录校验Filter晚OPTIONS预检请求被登录Filter直接拦截了根本没走到CORS Filter跨域头自然就没往响应里写。正确做法是把CORS Filter的order设为最高优先级数字最小登录Filter或者其他鉴权Filter放在它后面。同样的问题也出现在编码Filter上如果字符编码Filter不是第一个请求体在后续解析时可能已经乱码。所以Filter顺序的优先级安排基本是编码Filter → CORS Filter → 日志Filter → 鉴权Filter → 业务相关Filter。另外一个值得留意的点Spring Boot内嵌Tomcat的Filter顺序和传统外置Tomcat部署在某些场景下可能会微调所以生产环境如果出现了测试环境没有的Filter顺序问题先检查两边部署方式是否一致。结尾写到这里SpringMVC的核心脉络差不多已经清晰了DispatcherServlet是总调度Controller是业务入口Interceptor是框架内的扩展点Filter是Servlet层的拦截关卡。你如果能把一次请求走到哪个组件这一条链路理顺遇到大多数Web层的问题基本都能一眼看穿症结所在。我个人在实际操作中的体会是学SpringMVC最重要的不是死记注解和配置项而是先在大脑里建立请求处理的整体图谱——从哪里进来、经过哪些关卡、每道关卡负责什么、异常在哪个环节抛出。图谱一旦建立后面学习Spring Security、Spring Cloud Gateway这类更上层的框架时思维方式是通用的因为你已经在用组件化、责任链的视角看Web请求了。最后再分享一个小习惯每次接到一个新项目我先花十分钟把项目的全局配置类、拦截器注册、过滤器列表全部列出来画一张简陋的请求流程图贴在自己工作区的文档里后续调试问题时效率真的能高一截。SpringMVC的坑和技巧远不止这些但上面聊的这些是我觉得每个Java后端都得掌握的底子。下一篇我打算写写SpringMVC和Spring Boot自动配置的底层关系如果你想看的话可以先动手验证一下今天聊的拦截器执行顺序毕竟这种东西自己跑一遍比看十遍文章都管用。
返回列表