前言
前些天,在项目实践过程中出现了一个奇怪的状况,Spring Boot 的参数绑定失效了。而起因只是因为同事把参数上的 @RequestParam 注解去掉了。我们都知道,如果参数名称和 Controller 的方法名相同时,根本不需要 @RequestParam 注解的,Spring Boot 会自动帮我们完成参数的绑定。但为什么自动绑定机制失效了呢?本篇文章会为大家揭开谜底,在此过程中也会全面讲解如何在 Spring Boot 项目中自定义配置 WebMvc,以及这其中的很多坑。
SpringBoot 自定义 WebMvc
Spring Boot 为 Spring MVC 提供了默认的配置主要包括视图解析器、静态资源处理、类型转化器与格式化器、HTTP 消息转换器、静态主页支持等,可谓简单易用。但实践中,难免需要进行个性化的配置,因此自定义 Web MVC 配置在所难免。Spring Boot 先后提供了 WebMvcConfigurerAdapter、WebMvcConfigurationSupport、WebMvcConfigurer、@EnableWebMvc 等形式来实现 Web MVC 的自定义配置。下面我们来逐一学习。
被废弃的 WebMvcConfigurerAdapter
在 Spring Boot1.0 + 中,可以使用 WebMvcConfigurerAdapter 来扩展 Spring MVC 的功能。WebMvcConfigurerAdapter 是 WebMvcConfigurer 的一个抽象实现类,该抽象类中所有的方法实现都为空,子类需要哪些功能就实现哪些功能。
到了 Spring 5.0 之后,也就是在 Spring Boot2.0 版本中,JDK 基于 Java8 来实现了,而在 Java8 中可以将接口的方法定义为 default。接口中被定义为 default 的方法子类可以不进行实现。而接口 WebMvcConfigurer 便运用了 Java8 的特性,因此 WebMvcConfigurerAdapter 存在的意义没有了。
于是,在 Spring Boot2.0 版本中,WebMvcConfigurerAdapter 这个类被弃用了。
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
查看 WebMvcConfigurerAdapter 的实现,你会发现它就是把接口的所有方法实现为一个空的方法而已,Java8 的 default 特性完全覆盖掉此功能。如果你是基于 Spring Boot 2.x 进行开发,通过 extends(继承)WebMvcConfigurerAdapter 来实现功能,那么可以直接替换为 implements(实现)WebMvcConfigurer 的形式了。关于具体实现,我们后面会讲到。
会覆盖的 WebMvcConfigurationSupport
WebMvcConfigurerAdapter 被废弃了,那么我们还可以通过继承 WebMvcConfigurationSupport 来实现 Spring MVC 的拓展。
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {...}
这个类很特殊,实现了 ApplicationContextAware 和 ServletContextAware 接口, 提供了一些默认实现,同时提供了很多 @Bean 方法,但是并没有提供 @Configureation 注解,因此这些 @Bean 并不会生效,所以我们需要继承这个类,并在提供的类上提供 @Configureation 注解才能生效。
WebMvcConfigurationSupport 中不仅定义了 Bean,还提供了大量 add、config 开头的方法。对照 WebMvcConfigurer 的方法定义,会发现几乎 WebMvcConfigurer 有的在 WebMvcConfigurationSupport 中都有。需要注意的是,某些方法在 WebMvcConfigurationSupport 中也并未实现具体功能。
比如常见的 addInterceptors 和 addViewControllers:
/**
* Override this method to add Spring MVC interceptors for
* pre- and post-processing of controller invocation.
* @see InterceptorRegistry
*/
protected void addInterceptors(InterceptorRegistry registry) {
}
/**
* Override this method to add vi