SpringBoot非常适合Web应用程序开发。您可以使用嵌入式Tomcat、Jetty、Undertow或Netty创建一个独立的HTTP服务器。大多数Web应用程序使用Spring Boot Starter Web模块快速启动和运行。您还可以选择使用SpringBootStarterWebFlux模块来构建反应式Web应用程序。
SpringWebMVC 框架(通常简称为“SpringMVC”)是一个富的“MVC” 的Web框架。SpringMVC允许您创建特殊的@controller或@restcontroller bean来处理传入的HTTP请求。控制器中的方法通过使用@requestmapping annotations映射到HTTP。
下面的代码显示了一个典型的@restcontroller,它为JSON数据提供服务:
@RestController
@RequestMapping(value="/users")
public class MyRestController {
@RequestMapping(value="/{user}", method=RequestMethod.GET)
public User getUser(@PathVariable Long user) {
// ...
}
@RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
List getUserCustomers(@PathVariable Long user) {
// ...
}
@RequestMapping(value="/{user}", method=RequestMethod.DELETE)
public User deleteUser(@PathVariable Long user) {
// ...
}
}
SpringBoot默认配置了SpringMVC
引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。
对静态资源的支持,包括对WebJars的支持。
自动注册Converter,GenericConverter,Formatter beans。
对HttpMessageConverters的支持。
自动注册MessageCodeResolver。
对静态index.html的支持。
对自定义Favicon的支持。
字段使用 ConfigurableWebBindingInitializer bean
注意点(重点结论):
(1)自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(是转发还是重定向)页面);
(2)ContentNegotiatingViewResolver:组合所有的视图解析器的;
(3)如何定制自己的视图解析器?我们可以自己给容器中添加一个视图解析器,SpringBoot会自动的将其组合进来;因为从源码分析可以返现ContentNegotiatingViewResolver所组合的视图解析器都是从容器中获取的。
分析一个日期格式化器部分源码:
@Bean
//在文件中配置日期格式化的规则,日期格式化器才会生效
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());
}
从源码中我们可以发现,仅有在配置文件中配置了,SpringBoot配置的日期格式化器才会生效。同时通过格式化器的注解@Bean
可以发现该组件在容器中,所以当我们自己需要自定义的格式化器,只需要将其加入容器中即可。(@Bena
)
(3):再来分析下HttpMessageConverters
,其主要作用是SpringMVC用来转换Http请求和响应的;User —> Json
看看WebMvcAutoConfiguration.class
中的函数:
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, @Lazy HttpMessageConverters messageConverters, ObjectProvider resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConverters = messageConverters;
this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
}
源码方法参数中@Lazy HttpMessageConverters messageConverters可以看出messageConverters是通过容器懒加载获得的,所以也可以得出一个结论:要自定义消息转换器,只需要自己给容器中添加自定义的HttpMessageConverter即可。
(4):ConfigurableWebBindingInitializer : 其主要作用就是 初始化WebDataBinder;将请求的参数转化为对应的JavaBean,并且会结合上面的类型、格式转换一起使用。
查看WebMvcAutoConfiguration.class中函数源码:
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
try {
//从容器中获取
return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
} catch (NoSuchBeanDefinitionException var2) {
return super.getConfigurableWebBindingInitializer();
}
}
可以发现ConfigurableWebBindingInitializer
是从容器(beanFactory
)中获取到的,所以我们可以配置一个ConfigurableWebBindingInitializer
来替换默认的,只需要在容器中添加一个我们自定义的转换器即可。
二、扩展SpringMVC
我们再看一段springBoot官方文档关于SpringMvc自动配置的描述。
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.
其大体翻译就是:
1. 如果保留Spring Boot MVC特性,你只需添加(拓展)其他的额外的MVC配置(拦截器,格式化处 理器,视图控制器等)。你可以添加自己的WebMvcConfigurerAdapter类型 的 @Configuration 类,而不需要注解@EnableWebMvc 。
2. 如果希望使用自定义 的 RequestMappingHandlerMapping,RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver,你可以声明一 个WebMvcRegistrationsAdapter实例提供这些组件。
3. 如果想全面控制Spring MVC,你可以添加自己的@Configuration ,并使 用 @EnableWebMvc 注解。
现在我们尝试拓展一下SpringMvc的功能,要求来一个视图映射,将/hello请求映射视图success中,并且来一个拦截器拦截所有/hello请求。
这个功能如果是在springmvc中实现是这样的:
那根据上面SpringBoot的研究,在SpringBoot中该如何实现这个功能呢?
根据 上面原文的翻译,要拓展一个这样的功能大体的实现步骤是这样的:
①、编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;
②不能标注@EnableWebMvc;
这样就是既保留了所有的自动配置,也能用我们扩展的配置;
首先找到WebMvcConfigurerAdapter.class源码,发现其实是一个抽象类,我们只需要实现我们要拓展的方法即可:
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
//这就是配置视图映射的方法
public void addViewControllers(ViewControllerRegistry registry) {
}
//这就是配置拦截器的方法
public void addInterceptors(InterceptorRegistry registry) {
}
}
下面就来实现我们的拓展(只拓展了视图映射功能,拦截器功能你们自行实现哈):
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /hello请求来到 success
registry.addViewController("/hello").setViewName("success");
}
}
拓展的原理:
1)、WebMvcAutoConfiguration是SpringMVC的自动配置类;
2)、自动配置类在做其他自动配置时会导入@Import(EnableWebMvcConfiguration.class)
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
可以看出EnableWebMvcConfiguration.class是自动配置类的一个内部类。并且继承了DelegatingWebMvcConfiguration
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration
再来看看DelegatingWebMvcConfiguration源码中的函数setConfigurers
//从容器中获取所有的WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
}
函数中setConfigurers中可以看到,其将容器中所有的WebMvcConfigurer配置都获取到了。
3)、容器中所有的WebMvcConfigurer都会一起起作用;
4)、我们的配置类也会被调用;
最终得到的效果就是:SpringMVC的自动配置和我们的扩展配置都会起作用;
三、全面控制SpringMVC配置
SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了。
从上面的官方文档翻译可以得知,我们只需要在配置类中添加@EnableWebMvc即可。
如我们将上面拓展的配置加上@EnableWebMvc注解;
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//浏览器发送 /hello请求来到 success
registry.addViewController("/hello").setViewName("success");
}
}
现在可以再启动项目就可以发现,现在springmvc的自动配置都失效了(静态映射、webjars等功能)。
全面控制的原理:
为什么@EnableWebMvc自动配置就失效了?
(1)观察其源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface EnableWebMvc {
}
(2)发现@EnableWebMvc的核心就是导入@Import({DelegatingWebMvcConfiguration.class})而发现DelegatingWebMvcConfiguration又是继承了WebMvcConfigurationSupport类。
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
(3)再回头看看springBoot对MVC的自动配置类WebMvcAutoConfiguration的源码中:
@Configuration
@ConditionalOnWebApplication( type = Type.SERVLET )
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//当不存在'WebMvcConfigurationSupport.class'的时候自动配置才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
.....略
}
从自动配置类WebMvcAutoConfiguration可以看出当且仅当不存在WebMvcConfigurationSupport.class的时候自动配置才生效,但是因为我们将拓展的配置类加上了@EnableWebMvc导致存在类WebMvcConfigurationSupport.class
(4)因为导入的WebMvcConfigurationSupport只有SpringMVC最基本的功能,而SpringMvc的其他自动配置功能失效,所以被我们的配置全面控制。
四、总结
1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(如ViewResolver)将用户配置的和自己默认的组合起来;
2)、用户可以借助添加 WebMvcConfigurerAdapter类型的 @Configuration 类,而不需要注解@EnableWebMvc来拓展Springmvc的自定义配置。
3)、用户可以在配置类加上了@EnableWebMvc注解,实现全面控制SpringMvc配置。