接口说明

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

配置路径匹配方式,方法签名:

1
default void configurePathMatch(PathMatchConfigurer configurer) {}

以PathMatchConfigurer做为参数进行配置

configurer.setUseTrailingSlashMatch()

是否使用/进行匹配,默认情况Spring会将 user 和/user 看作相同的路径,可通过此配置来调整定义其它行为,默认为 true,如果想匹配为不同的路径,可将此值设置为 false

configurer.addPathPrefix(String prefix, Predicate<Class<?>> predicate)

配置全局路径前缀,此配置可以SpringMVC请求中统一加上相同的前缀,支持条件判断

  • 对所有接口参数都增加/api请求前缀,则可配置:

    1
    
    configurer.addPathPrefix("/api", true);
    
  • 只对RestController接口增加/api请求前缀

    1
    
    configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
    

addInterceptors

为请求增加统一拦截器,方法签名:

1
public void addInterceptors(InterceptorRegistry registry) {}

以InterceptorRegistry做为入参

registry.addInterceptor()

注册标准 Spring的HandlerInterceptor接口,对请求进行拦截处理,返回InterceptorRegistration封装类,支持配置模式匹配与排除路径、拦截器顺序

1
2
3
4
5
6
7
8
9
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封装类,支持配置模式匹配与排除路径、拦截器顺序

1
2
3
4
5
6
7
8
9
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

为请求参数增加解析器,方法签名:

1
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}

参数为 HandlerMethodArgumentResolver 列表,需要自定义的参数解析类添加后,可以在Controller请求参数列表中进行使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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

为请求增加跨域配置, 方法签名:

1
public void addCorsMappings(CorsRegistry registry) {}

以 CorsRegistry为入参,支持对指定url跨域名进行控制,包含对方法、Header、Origin来源的控制

1
2
3
4
5
6
7
@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/api/v1/**")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedOrigins("*")
            .allowedHeaders("*");
}

addReturnValueHandlers

自定义返回值配置,方法签名:

1
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}

参数为 HandlerMethodReturnValueHandler 列表,可以针对特定的返回值类型进行自定义操作

1
2
3
4
5
6
7
@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对象,方法签名:

1
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}

Spring中自带的转换器都在 org.springframework.http.converter 包下,包含了常用的消息转换方式,可以自定义配置一个JSON转换器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@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

全局异常处理器配置,方法签名:

1
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}

默认情况此列表为空,如果没有配置则Spring容器会给配置默认的处理集合

从上图可知,Spring增加了3个默认的 HandlerExceptionResolvers:

  1. ExceptionHandlerExceptionResolve
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

自定义ExceptionResolver配置样例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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