上次我们快速搭建了一个Spring Boot项目,我们只需添加业务逻辑就能直接运行访问了,说明Spring Boot已经自动为我们做完了配置工作,这次我们就来看看具体是哪些工作,如果我们想接管配置又该怎么做。
查看WebMvcAutoConfiguration
和WebMvcProperties
的源码,可以发现Spring MVC全局配置以spring.mvc
开头,而Spring Boot提供了如下配置
优先级最高,自己不处理View,而是将View代理给其他ViewResolver处理
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(
beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
根据Controller中方法的返回值来查找ViewResolver处理View
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
这个很常见了,设置视图路径前后缀
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
上篇文章我们添加了Thymeleaf,就自动为我们配置了前后缀。
在addResourceHandlers
方法中可以看到有以下静态资源的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Integer cachePeriod = this.resourceProperties.getCachePeriod();
if (!registry.hasMappingForPattern("/webjars/**")) {
//映射webjar路径为 /webjars/**,可直接访问,有关webjar的内容可访问 www.webjars.org
customizeResourceHandlerRegistration(
registry.addResourceHandler("/webjars/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/")
.setCachePeriod(cachePeriod));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
//将"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/",统统映射为 "/**",可以直接访问
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(
this.resourceProperties.getStaticLocations())
.setCachePeriod(cachePeriod));
}
}
查看Formatter配置源码
@Override
public void addFormatters(FormatterRegistry registry) {
for (Converter, ?> converter : getBeansOfType(Converter.class)) {
registry.addConverter(converter);
}
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
registry.addConverter(converter);
}
for (Formatter> formatter : getBeansOfType(Formatter.class)) {
registry.addFormatter(formatter);
}
}
可以看到只要我们定义了Converter
、GenericConverter
、Formatter
实现类的Bean,就会自动注册到Spring MVC中。
@Override
public void configureMessageConverters(List> converters) {
converters.addAll(this.messageConverters.getConverters());
}
除了默认的StringHttpMessageConverter
、ByteArrayHttpMessageConverter
等,Spring MVC默认使用MappingJackson2HttpMessageConverter
和MappingJackson2XmlHttpMessageConverter
,如果要新增自定义的HttpMessageConverter
,只需定义一个HttpMessageConverters
的bean即可
private WelcomePageHandlerMapping(Resource welcomePage,
String staticPathPattern) {
if (welcomePage != null && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName("forward:index.html");
setRootHandler(controller);
setOrder(0);
}
}
在以下目录放置index.html
会自动设为首页
如果要完全接管Web配置,只需新建一个配置类(注解@Configuration
@EnableWebMvc
),然后就能实现自己完全控制的Spring MVC。如果要保留自动配置并新增额外配置的话也很简单,定义一个配置类(注解@Configuration
,不需要 @EnableWebMvc
)继承WebMvcConfigurerAdapter
,就能在保留自动配置的基础上添加自己想要的配置了,以下是个小例子
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//此方法不会覆盖自动配置,而是额外添加
registry.addViewController("/login").setViewName("/login");
}
@Override
public void configureMessageConverters(List> converters) {
//配置fastjson替换jackson
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter ();
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setDefaultCharset(Charset.forName("UTF-8"));
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.PrettyFormat,
SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
//添加字符串解析器,防止返回字符串时多出双引号
converters.add(stringConverter);
converters.add(converter);
}
}
将Servlet
、Filter
、Listener
声明为Bean,或者注册ServletRegistrationBean
、FilterRegistrationBean
、ServletListenerRegistrationBean
的Bean
//直接注册Bean
@Bean
public XxServlet xxServlet(){
return new XxServlet();
}
@Bean
public XxServlet xxServlet(){
return new XxServlet();
}
@Bean
public XxFilter xxFilter(){
return new XxFilter();
}
//通过RegistrationBean注册
@Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new XxServlet(),"/xx/*");
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new YyFilter());
registrationBean.setOrder(2);
return registrationBean;
}
@Bean
public ServletListenerRegistrationBean zzListenerServletRegistrationBean(){
return new ServletListenerRegistrationBean(new ZzListener());
}
本文介绍了Spring Boot关于Spring MVC的自动配置项,这样我们在开发时也能根据业务需要手动修改配置,以实现我们想要的功能。
参考文献:
《javaee开发的颠覆者 Spring Boot实战》