上篇Blog详细探讨了SpringBoot是如何整合数据源的,从JDBC层面、数据源连接池层面(Druid),再到MyBatis层面。我们分别使用了spring-boot-starter-data-jdbc、druid-spring-boot-starter、mybatis-spring-boot-starter
这三个封装好的场景启动器,通过SpringBoot的自动配置机制我们只需要关心yml配置文件内容即可简单方便的使用SpringBoot操作数据库,可以说非常方便,至少比起Spring整合MyBatis节省了不少代码和配置。同样SpringBoot整合SpringMVC也非常方便,通过spring-boot-starter-web
场景启动器即可。
spring-boot-starter-web场景启动器
spring-boot-starter-web 为我们提供了嵌入的 Servlet 容器以及 SpringMVC 的依赖,并为 Spring MVC 提供了大量自动配置,可以适用于大多数 Web 开发场景。Spring Boot 为 Spring MVC 提供了自动配置,并在 Spring MVC 默认功能的基础上添加了以下特性:
- 引入了 ContentNegotiatingViewResolver 和 BeanNameViewResolver(视图解析器)
- 对包括 WebJars 在内的静态资源的支持,支持对静态首页(index.html)的访问
- 自动注册 Converter、GenericConverter 和 Formatter (转换器和格式化器)
- 对 HttpMessageConverters 的支持(Spring MVC 中用于转换 HTTP 请求和响应的消息转换器)
- 自动注册 MessageCodesResolver(用于定义错误代码生成规则)
- 自动使用 ConfigurableWebBindingInitializer
只要我们在 Spring Boot 项目中的 pom.xml 中引入了 spring-boot-starter-web ,即使不进行任何配置,也可以直接使用 Spring MVC 进行 Web 开发,如果大家有没有印象可以看下我的第一篇SpringBoot的Blog【SpringBoot学习笔记 一】SpringBoot基本概念和项目初始化,当时第一次引入web模块的时候其实已经自动引入了SpringMVC。对比之前的Spring整合SpringMVC,以下步骤都省略了:
我们专注于自己的逻辑即可,SpringBoot的自动配置都替我们做好了。
SpringMVC自动配置原理
Spring Boot 抛弃了传统 xml 配置文件,通过配置类(标注 @Configuration 的类,相当于一个 xml 配置文件)以 JavaBean 形式进行相关配置。Spring Boot 对 Spring MVC 的自动配置可以满足我们的大部分需求,但是我们也可以通过自定义配置类(标注 @Configuration 的类)并实现 WebMvcConfigurer 接口来定制 Spring MVC 配置,例如拦截器、格式化程序、视图控制器等
WebMvcAutoConfiguration自动配置类
在自动配置原理SpringBoot学习笔记 四】SpringBoot自动配置原理这篇Blog中提到,在 spring-boot-autoconfigure-xxx.jar
类路径下的 META-INF/spring.factories
中设置了一些自动配置类,就包括WebMvcAutoConfiguration
它可以帮我们完成SpringMVC的自动整合和定制。
格式化举例说明
我们举个例子,找到格式化转换器,代码如下:
@Bean public FormattingConversionService mvcConversionService() { WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat()); this.addFormatters(conversionService); return conversionService; }
跟进去可以看到:
public String getDateFormat() { return this.dateFormat; } /** * Date format to use. For instance, `dd/MM/yyyy`. 默认的 */ private String dateFormat;
可以看到在我们的Properties文件中,我们可以进行自动配置它,如果配置了自己的格式化方式,就会注册到Bean中生效,例如我们可以在配置文件中配置日期格式化的规则。
扩展SpringMVC功能
在 Spring Boot 项目中,我们可以通过以下 2 中形式定制 Spring MVC:扩展 Spring MVC,全面接管 Spring MVC
下面,我们分别对这两种定制 Spring MVC 的形式进行介绍
WebMvcConfigurer 是一个基于 Java 8 的接口,该接口定义了许多与 Spring MVC 相关的方法,其中大部分方法都是 default 类型的,且都是空实现。因此我们只需要定义一个配置类实现 WebMvcConfigurer 接口,并重写相应的方法便可以定制 Spring MVC 的配置
WebMvcConfigurer接口
如果 Spring Boot 对 Spring MVC 的自动配置不能满足我们的需要,我们还可以通过自定义一个 WebMvcConfigurer 类型(实现 WebMvcConfigurer 接口)的配置类(标注 @Configuration,但不标注 @EnableWebMvc 注解的类),来扩展 Spring MVC。这样不但能够保留 Spring Boot 对 Spring MVC 的自动配置,享受 Spring Boot 自动配置带来的便利,还能额外增加自定义的 Spring MVC 配置
定制修改SpringBoot的默认配置
我们可以修改SpringBoot的默认配置来自定义自己的实现,我们先来看下如果不开启拦截,首页请求会是什么样:
1 创建Controller
我们创建一个result的Controller跳转请求:
package com.example.springboot.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ResultController { @GetMapping("/result") public String loginPage() { return "result"; } }
2 修改自定义配置
实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
package com.example.springboot.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; //实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { //当访问 “/” 或 “/index.html” 时,都直接跳转到登陆页面 registry.addViewController("/").setViewName("result"); } }
修改自定义配置,让首页请求到来时,会被拦截然后转到result的请求上去
package com.example.springboot.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ResultController { @GetMapping("/result") public String loginPage() { return "我不是你想看到的首页,我是一个跳转定制页面,一个登录页面"; } }
3 请求首页查看效果
我们配置了自定义设置后再次进行首页请求查看:
扩展SpringMVC原理
为什么定制扩展可以生效呢?我们通过源码追溯一下:
WebMvcAutoConfiguration
是 SpringMVC的自动配置类,里面有一个类
WebMvcAutoConfigurationAdapter
这个类上有一个注解,在做其他自动配置时会导入:@Import(EnableWebMvcConfiguration.class)
,我们点进EnableWebMvcConfiguration
这个类看一下,它继承了一个父类:
DelegatingWebMvcConfiguration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); // 从容器中获取所有的webmvcConfigurer @Autowired(required = false) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); } } }
我们可以在这个类中去寻找一个我们刚才设置的viewController当做参考,发现它调用了一个
protected void addViewControllers(ViewControllerRegistry registry) { this.configurers.addViewControllers(registry); }
继续向下看:
public void addViewControllers(ViewControllerRegistry registry) { Iterator var2 = this.delegates.iterator(); while(var2.hasNext()) { WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next(); delegate.addViewControllers(registry); } }
所以得出结论:所有的WebMvcConfiguration都会被作用,不止Spring自己的配置类,我们自己的配置类当然也会被调用
全面接管SpringMVC
全面接管即SpringBoot对SpringMVC的自动配置不需要了,所有内容都是我们自己去配置,实现方式只需在我们的配置类中加一个@EnableWebMvc
,当然我们开发中,不推荐使用全面接管SpringMVC。
开启全面接管效果
开启全面接管比较简单,我们只需要在原有的定制配置类开启注解即可:
package com.example.springboot.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; //实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能 @Configuration @EnableWebMvc public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { //当访问 “/” 或 “/index.html” 时,都直接跳转到登陆页面 registry.addViewController("/").setViewName("result"); } }
全面接管后再次请求:
Spring Boot 能够访问首页静态资源是SpringBoot默认的SpringMVC配置,全面接管后该自动配置失效
全面接管的原理
为什么加了一个注解,自动配置就失效了?我们看下源码:
EnableWebMVC注解
@Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { }
这里发现它是导入了一个类,我们可以继续进去看
WebMvcConfigurationSupport类
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { // ...... }
它继承了一个父类 WebMvcConfigurationSupport,我们来回顾一下Webmvc自动配置类
@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) // 这个注解的意思就是:容器中没有这个组件的时候,这个自动配置类才生效 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { }
因为有了这个注解@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
,导致
WebMvcAutoConfiguration
自动配置不会生效,而WebMvcConfigurationSupport只有SpringMVC的基本功能,所以我们编写起来难度太大,不方便操作。
总结一下
首先,不要用全面接管,不要用全面接管,不要用全面接管,重要的事情说三遍,这样啥活都干不了了。其次我们可以发现定制的SpringMVC配置有点像拦截器,就是我们可以在默认的基础上修改请求,常用的一些功能还是很有用的,所以只要了解定制扩展方式就可以了,当然为什么全面接管生效的原理也需要简单了解下,知其然知其所以然,最后我们最好用封装好的场景启动器,例如:spring-boot-starter-web
可以给我们省去很多麻烦。