七、SpringBoot之SpringMVC自动配置原理
1、Spring MVC 自动配置
Spring Boot 自动配置好了SpringMVC
以下是SpringBoot对SpringMVC的默认配置:都在WebMvcAutoConfiguration.java这个类里
- 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染,是转发还是重定向。
例如:ContentNegotiatingViewResolver:组合所有的视图解析器的;
WebMvcAutoConfiguration.java
@Bean //给容器中添加ContentNegotiationManager组件
@ConditionalOnBean({ViewResolver.class})
@ConditionalOnMissingBean(
name = {"viewResolver"},
value = {ContentNegotiatingViewResolver.class}
)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(-2147483648);
return resolver;
}
//初始化方法
protected void initServletContext(ServletContext servletContext) {
//用BeanFactoryUtils工具类从容器中获取所有的视图解析器,这个解析器就作为它解析的所有解析器
Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
ViewResolver viewResolver;
if (this.viewResolvers == null) {
this.viewResolvers = new ArrayList(matchingBeans.size());
Iterator var3 = matchingBeans.iterator();
while(var3.hasNext()) {
viewResolver = (ViewResolver)var3.next();
if (this != viewResolver) {
this.viewResolvers.add(viewResolver);
}
}
} else {
for(int i = 0; i < this.viewResolvers.size(); ++i) {
viewResolver = (ViewResolver)this.viewResolvers.get(i);
if (!matchingBeans.contains(viewResolver)) {
String name = viewResolver.getClass().getName() + i;
this.obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolver, name);
}
}
}
if (this.viewResolvers.isEmpty()) {
this.logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the 'viewResolvers' property on the ContentNegotiatingViewResolver");
}
AnnotationAwareOrderComparator.sort(this.viewResolvers);
this.cnmFactoryBean.setServletContext(servletContext);
}
ContentNegotiatingViewResolver.java
@Nullable
//解析视图
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if (requestedMediaTypes != null) {
//获取候选视图对象
List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
//选择适合的视图对象,然后把这个对象返回
View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
if (this.useNotAcceptableStatusCode) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
}
return NOT_ACCEPTABLE_VIEW;
} else {
this.logger.debug("No acceptable view found; returning null");
return null;
}
}
//获取候选视图对象
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
List<View> candidateViews = new ArrayList();
if (this.viewResolvers != null) {
Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
//把所有视图解析器拿来遍历,挨个解析
Iterator var5 = this.viewResolvers.iterator();
while(var5.hasNext()) {
ViewResolver viewResolver = (ViewResolver)var5.next();
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
candidateViews.add(view);
}
Iterator var8 = requestedMediaTypes.iterator();
while(var8.hasNext()) {
MediaType requestedMediaType = (MediaType)var8.next();
List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
Iterator var11 = extensions.iterator();
while(var11.hasNext()) {
String extension = (String)var11.next();
String viewNameWithExtension = viewName + '.' + extension;
view = viewResolver.resolveViewName(viewNameWithExtension, locale);
if (view != null) {
candidateViews.add(view);
}
}
}
}
}
if (!CollectionUtils.isEmpty(this.defaultViews)) {
candidateViews.addAll(this.defaultViews);
}
return candidateViews;
}
如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;
@Bean
public ViewResolver myViewReolver(){
return new MyViewResolver();
}
private static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
- 自动配置了静态资源文件夹路径,webjars(前面用过)
- 自动配置了静态首页访问(index.html)
- 自动配置来favicon.ico图标访问
- Converter:转换器;类型转换使用Converter
- Formatter 格式化器;比如日期格式化转换
//添加格式化器
public void addFormatters(FormatterRegistry registry) {
//从容器中获取所有的Converter,再遍历
Iterator var2 = this.getBeansOfType(Converter.class).iterator();
while(var2.hasNext()) {
Converter<?, ?> converter = (Converter)var2.next();
//自己添加的格式化器转换器,我们只需要放在容器中即可
registry.addConverter(converter);
}
//从容器中获取所有的GenericConverter,再遍历
var2 = this.getBeansOfType(GenericConverter.class).iterator();
while(var2.hasNext()) {
GenericConverter converter = (GenericConverter)var2.next();
registry.addConverter(converter);
}
//从容器中获取所有的Formatter,再遍历
var2 = this.getBeansOfType(Formatter.class).iterator();
while(var2.hasNext()) {
Formatter<?> formatter = (Formatter)var2.next();
registry.addFormatter(formatter);
}
}
- HttpMessageConverter:SpringMVC用来转换Http请求和响应的;例如User转换成Json;
//只有一个有参构造器的情况下,每一个参数的值都是要从容器中拿
//HttpMessageConverters 是从容器中确定,获取所有的HttpMessageConverter;
//自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
}
-
MessageCodesResolver:
定义错误代码生成规则
public MessageCodesResolver getMessageCodesResolver() {
//从配置类中拿到一个配置,定义错误代码生成规则的配置
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
} else {
return null;
}
}
-
ConfigurableWebBindingInitializer
:初始化WebDataBinder;例如:请求数据与JavaBean绑定
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
try {
//从容器中拿到ConfigurableWebBindingInitializer
//我们可以配置一个ConfigurableWebBindingInitializer添加到容器里来替换默认的;
return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
} catch (NoSuchBeanDefinitionException var2) {
//拿不到调用父类的ConfigurableWebBindingInitializer
return super.getConfigurableWebBindingInitializer();
}
}
web的所有自动场景都在org.springframework.boot.autoconfigure.web这个包里。
2、扩展SpringMVC
- Spring以前需要写配置文件
<!--Spring视图映射配置-->
<mvc:view-controller path="/hello" view-name="success"/>
<!--定义Spring的拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
- SpringBoot不用写配置文件,编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc;
SpringBoot扩展SpringMVC既保留了所有的自动配置,也能用我们扩展的配置;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
//WebMvcConfigurerAdapter实现WebMvcConfigurer接口,实现了接口里的方法都是空方法
//自定义配置类想配置哪个属性,就重写对应的方法就好了
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//super.addViewControllers(registry);
//浏览器发送 /atguigu请求来到success页面
registry.addViewController("/atguigu").setViewName("success");
}
}
- 原理:
1.WebMvcAutoConfiguration是SpringMVC的自动配置类
2.在做其他自动配置时会导入@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
//WebMvcAutoConfiguration.java里有一个内部类WebMvcAutoConfigurationAdapter
//WebMvcAutoConfigurationAdapter实现WebMvcConfigurer接口,实现了接口里的方法
@Configuration
//在做其他自动配置时会导入@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
3.容器中所有的WebMvcConfigurer都会一起起作用;
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
public DelegatingWebMvcConfiguration() {}
@Autowired(
required = false
)
//从容器中获取所有的WebMvcConfigurer,然后把WebMvcConfigurer赋值到configurers里
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//其中的一个参考实现,将从容器中获取所有的WebMvcConfigurer相关配置都来一起调用
//容器中所有的WebMvcConfigurer都会一起起作用;
//我们自己的配置类也会被调用
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
4.我们的配置类也会被调用;
效果:SpringMVC的自动配置和我们的扩展配置都会起作用;
3、全面接管SpringMVC
- @EnableWebMvc
SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了,我们值需要在配置类中添加@EnableWebMvc即可;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@EnableWebMvc //全面接管SpringMVC,所有的SpringMVC的自动配置都失效了
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//super.addViewControllers(registry);
//浏览器发送 /atguigu请求来到success页面
registry.addViewController("/atguigu").setViewName("success");
}
}
- 原理:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
2.SpringMVC自动配置类表示容器中没有WebMvcConfigurationSupport这个组件的时候,这个自动配置类才生效,@EnableWebMvc将WebMvcConfigurationSupport组件导入进来;所以所有的SpringMVC的自动配置都失效了,只导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
3.SpringMVC自动配置类表示容器中没有WebMvcConfigurationSupport这个组件的时候,这个自动配置类才生效
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//容器中没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
4、如何修改SpringBoot的默认配置
模式:
- SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
- 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
- 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置