接口说明
WebMvcConfigurer是Spring提供的统一配置接口,用于自定义Spring MVC的配置,全局更改服务的行为,提供的可配置项:

| 方法名称 | 参数 | 用途 |
|---|---|---|
| addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) | List<HandlerMethodArgumentResolver> argumentResolvers | 添加自定义的 HandlerMethodArgumentResolver 用于解析方法参数。 |
| addCorsMappings(CorsRegistry registry) | CorsRegistry registry | 配置跨域资源共享 (CORS) |
| addFormatters(FormatterRegistry registry) | FormatterRegistry registry | 添加自定义的格式化程序和转换器 |
| addInterceptors(InterceptorRegistry registry) | InterceptorRegistry registry | 添加自定义的拦截器 |
| addRequestMappings(RequestMappingInfo customization, Class<Controller> controllerClass) | RequestMappingInfo customization, Class<Controller> controllerClass | 关联自定义请求映射到具控制器类 |
| addResourceHandlers(ResourceHandlerRegistry registry) | ResourceHandlerRegistry registry | 配置资源处理,如静态资源路径 |
| addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) | List<HandlerMethodReturnValueHandler> returnValueHandlers | 添加自定义的 HandlerMethodReturnValueHandler 用于处理方法返回值 |
| addViewControllers(ViewControllerRegistry registry) | ViewControllerRegistry registry | 注册视图控制器 |
| configureAsyncSupport(AsyncSupportConfigurer configurer) | AsyncSupportConfigurer configurer | 配置异步支持 |
| configureContentNegotiation(ContentNegotiationConfigurer configurer) | ContentNegotiationConfigurer configurer | 配置内容协商策略 |
| configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) | DefaultServletHandlerConfigurer configurer | 启用默认 Servlet 处理 |
| configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) | List<HandlerExceptionResolver> exceptionResolvers | 配置异常处理程序 |
| configureMessageConverters(List<HttpMessageConverter<?>> converters) | List<HttpMessageConverter<?>> converters | 配置自定义的 HttpMessageConverter |
| configurePathMatch(PathMatchConfigurer configurer) | PathMatchConfigurer configurer | 配置路径匹配规则 |
| configureViewResolvers(ViewResolverRegistry registry) | ViewResolverRegistry registry | 配置视图解析器 |
| extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) | List<HandlerExceptionResolver> exceptionResolvers | 扩展异常处理程序 |
| extendMessageConverters(List<HttpMessageConverter<?>> converters) | List<HttpMessageConverter<?>> converters | 扩展自定义的 HttpMessageConverter |
| extendInterceptors(List<HandlerInterceptor> interceptors) | List<HandlerInterceptor> interceptors | 扩展拦截器 |
| extendArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) | List<HandlerMethodArgumentResolver> argumentResolvers | 扩展自定义的 HandlerMethodArgumentResolver 用于解析方法参数。 |
| extendReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) | List<HandlerMethodReturnValueHandler> returnValueHandlers | 扩展自定义的 HandlerMethodReturnValueHandler 用于处理方法返回值。 |
| extendViewResolvers(List<ViewResolver> viewResolvers) | List<ViewResolver> viewResolvers | 扩展视图解析器 |
关联类
WebMvcConfigurationSupport
EnableWebMvc
接口常用方法
接口提供了将近20个方法对Spring的配置进行调整,本次只针对常用的Rest接口开发相关内容进行展开学习研究
configurePathMatch
配置路径匹配方式,方法签名:
default void configurePathMatch(PathMatchConfigurer configurer) {}
以PathMatchConfigurer做为参数进行配置
configurer.setUseTrailingSlashMatch()
是否使用/进行匹配,默认情况Spring会将 user 和/user 看作相同的路径,可通过此配置来调整定义其它行为,默认为 true,如果想匹配为不同的路径,可将此值设置为 false
configurer.addPathPrefix(String prefix, Predicate<Class<?>> predicate)
配置全局路径前缀,此配置可以SpringMVC请求中统一加上相同的前缀,支持条件判断
对所有接口参数都增加/api请求前缀,则可配置:
configurer.addPathPrefix("/api", true);
只对RestController接口增加/api请求前缀
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
addInterceptors
为请求增加统一拦截器,方法签名:
public void addInterceptors(InterceptorRegistry registry) {}
以InterceptorRegistry做为入参
registry.addInterceptor()
注册标准 Spring的HandlerInterceptor接口,对请求进行拦截处理,返回InterceptorRegistration封装类,支持配置模式匹配与排除路径、拦截器顺序
public class AccessReportInterceptor implements HandlerInterceptor{...}
@Resource
private AccessReportInterceptor accessReportInterceptor;
registry.addInterceptor(this.accessReportInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/error", URL_PREFIX + "/system/**")
.order(ACCESS_REPORT);
registry.addWebRequestInterceptor()
注册通用Web请求拦截,包含Servlet请求,返回InterceptorRegistration封装类,支持配置模式匹配与排除路径、拦截器顺序
public class MyWebInterceptor implements WebRequestInterceptor {...}
@Resource
private MyWebInterceptor myWebInterceptor;
registry.addWebRequestInterceptor(this.myWebInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/error", URL_PREFIX + "/system/**")
.order(ACCESS_REPORT);
两种拦截器配置的区别

从图中看出 WebRequestInterceptor比 HandlerInterceptor 更抽象,更通用。在常见开发的SpringMVC应用中,优先选择使用与Spring集成更好的 HandlerInterceptor
addArgumentResolvers
为请求参数增加解析器,方法签名:
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}
参数为 HandlerMethodArgumentResolver 列表,需要自定义的参数解析类添加后,可以在Controller请求参数列表中进行使用
public class UserParamResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(UserParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
System.out.println("in userParam resolveArgument");
}
}
@RequestMapping("/detail")
public Response<BookDetailVO> detail(@RequestParam long bookId, @UserParam Long uid) {
return bookService.queryBookDetail(bookId, uid);
}
在用户请求 /detail接口时会先执行UserParamResolver中的resolveArgument的逻辑,对参数uid 进行赋值操作,Spring使用此方法支持了扩展自定义参数解析逻辑
addCorsMappings
为请求增加跨域配置, 方法签名:
public void addCorsMappings(CorsRegistry registry) {}
以 CorsRegistry为入参,支持对指定url跨域名进行控制,包含对方法、Header、Origin来源的控制
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/v1/**")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedOrigins("*")
.allowedHeaders("*");
}
addReturnValueHandlers
自定义返回值配置,方法签名:
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}
参数为 HandlerMethodReturnValueHandler 列表,可以针对特定的返回值类型进行自定义操作
@Resource
private MyReturnHandler myReturnHandler;
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
handlers.add(myReturnHandler);
}
经过以上配置,测试时发现代码逻辑不生效,因为Spring启动时会注入默认的 HandlerMethodReturnValueHandler ,只要这些Handler有一个返回true,后续的Handler就不会执行了。addReturnValueHandlers 的Doc也说明不会覆盖默认的 Handler,如果想覆盖,需要直接配置 RequestMappingHandlerAdapter 类(后续填坑)


与 CustomResponseBodyAdvice 的关系
两者都可以对返回值进行修改,HandlerMethodReturnValueHandler在控制执行后的第一步进行返回值的处理,而 CustomResponseBodyAdvice 在返回内容发送给客户端之前进行修改;所以会先执行 HandlerMethodReturnValueHandler逻辑,然后会执行 CustomResponseBodyAdvice 的操作
configureMessageConverters
配置自定义消息转换器,将JAVA序列化对象转换成JSON和XML对象,方法签名:
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
Spring中自带的转换器都在 org.springframework.http.converter 包下,包含了常用的消息转换方式,可以自定义配置一个JSON转换器
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 移除默认的 JSON 转换器
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
// 添加自定义配置的 Jackson 转换器
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(objectMapper);
jsonConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON));
converters.add(jsonConverter);
}
configureHandlerExceptionResolvers
全局异常处理器配置,方法签名:
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
默认情况此列表为空,如果没有配置则Spring容器会给配置默认的处理集合

从上图可知,Spring增加了3个默认的 HandlerExceptionResolvers:
- ExceptionHandlerExceptionResolve
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
自定义ExceptionResolver配置样例:
@Component
public class CustomHandlerExceptionResolver implements org.springframework.web.servlet.HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
try {
if (ex instanceof BusinessException) {
response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
} else {
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error");
}
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
@Resource
private CustomHandlerExceptionResolver customHandlerExceptionResolver;
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(customHandlerExceptionResolver);
}
目前最好的方式是使用 @ControllerAdvice 和 @ExceptionHandler 来更简洁地处理异常,如果同时使用了两种方式,则只会执行 configureHandlerExceptionResolvers 中配置的处理类
REF
https://juejin.cn/post/7098258522978123806?felosearch_translate=1