随笔分类
Spring MVC的自动配置
a.Spring MVC auto-configuration
Spring Boot自动配置好了MVC
以下是SpringBoot对SpringMVC的默认:
The auto-configuration adds the following features on top of Spring’s defaults:
-
Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.-
自动配置了ViewResolve(视图解析器:根据方法的返回值得到视图对象 View,通过对象决定如何来渲染页面(转发、重定向).
-
ContentNegotiatingViewResolve:组合所有容器中的所有视图解析器,再来一次遍历选择最适合的视图解析器 ---
/** * 部分源码解析 **/ //最开始从ioc(BeanFactory)中得到所有的ViewResolves @Override protected void initServletContext(ServletContext servletContext) { Collection
matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values(); if (this.viewResolvers == null) { this.viewResolvers = new ArrayList<>(matchingBeans.size()); for (ViewResolver viewResolver : matchingBeans) { if (this != viewResolver) { this.viewResolvers.add(viewResolver); } } } else { for (int i = 0; i < this.viewResolvers.size(); i++) { ViewResolver vr = this.viewResolvers.get(i); if (matchingBeans.contains(vr)) { continue; } String name = vr.getClass().getName() + i; obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name); } } AnnotationAwareOrderComparator.sort(this.viewResolvers); this.cnmFactoryBean.setServletContext(servletContext); } //解析视图名称 @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes"); List requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); if (requestedMediaTypes != null) { List candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); View bestView = getBestView(candidateViews, requestedMediaTypes, attrs); if (bestView != null) { return bestView; } } String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : ""; if (this.useNotAcceptableStatusCode) { if (logger.isDebugEnabled()) { logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo); } return NOT_ACCEPTABLE_VIEW; } else { logger.debug("View remains unresolved" + mediaTypeInfo); return null; } } private List getCandidateViews(String viewName, Locale locale, List requestedMediaTypes) throws Exception { List candidateViews = new ArrayList<>(); if (this.viewResolvers != null) { Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set"); for (ViewResolver viewResolver : this.viewResolvers) { //遍历得到candidateViews的值 View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { candidateViews.add(view); } for (MediaType requestedMediaType : requestedMediaTypes) { List extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType); for (String extension : extensions) { 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; } //从候选试图解析中择选出最适合的 private View getBestView(List candidateViews, List requestedMediaTypes, RequestAttributes attrs) { ... ... for (View candidateView : candidateViews) { ... } } - ==如何定制:我们可以自己给容器中添加一个视图解析器,自动地将其组合进来==
@Bean public ViewResolver MyViewResolve() { return new MyViewResolve(); } public static class MyViewResolve implements ViewResolver { @Override public View resolveViewName(String viewName, Locale locale) throws Exception { return null; } }
-
如图:
-
浏览器发送请求,debug DispatchServlet类的doDispatch方法!
-
-
Support for serving static resources, including support for WebJars (see below). 静态资源文件夹,webjars
-
自动注册了 of
Converter
,GenericConverter
,Formatter
beans.-
Converter :转换器,public String hello (User user) :类型转换用Converter (静态页面的数据要进行转换及其封装)
-
Formatter:格式化器; 2020-10-07 ===Date
@Bean @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规则 public Formatter
dateFormatter() { return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件 } 自己添加的格式化器,只需要放在容器中即可
-
-
Support for
HttpMessageConverters
(see below). SpringBoot用来转换Http请求和响应的 User -----JSONHttpMessageConverters
是从容器中确定;获取所有的HttpMessageConverter;- ==自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)==
-
Automatic registration of
MessageCodesResolver
(see below). 定义错误代码生成规则的 -
Static
index.html
support. 静态首页 -
Custom
Favicon
support (see below). Favicon.ico -
Automatic use of a
ConfigurableWebBindingInitializer
bean (see below).
==我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)==
/**
* Return the {@link ConfigurableWebBindingInitializer} to use for
* initializing all {@link WebDataBinder} instances.
初始化数据绑定
请求数据 ====JavaBean
*/
org.springframework.boot.autoconfigure.web :Web的所有自动配置场景
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
.
b.扩展SpringMVC
<mvc:view-controller path="/hello" view-name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
==使用@Configuration编写一个配置类,类型是WebMvcConfigurerAdapter,但是不能标注#EnableWebMvc==
看看源码后便会知道,SpringBoot使用这个适配器(含有大量空方法)带来了很多方法(适配器实现了WebMvcConfigurer接口) -- 还真是万物皆可适配 本柱np
==既保留了原有的所有配置,又新增了我们自己的配置==
/**
* 使用WebMvcConfiguerAdapter可以来扩展SpringMVC的功能
*/
@Configuration
public class MyWebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//super.addViewControllers(registry);
//浏览器发送 /liangye 跳转至 success页面
registry.addViewController("/liangye")
.setViewName("success");
}
原理:
-
WebMvcAutoConfiguration是SpringBoot的自定配置类
-
在做其他自动配置时会导入 @Import(EnableWebMvcConfiguration.class)
@Configuration public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); //从容器中获取所有的WebMvcConfigurer @Autowired(required = false) public void setConfigurers(List
configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); //一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用; @Override // public void addViewControllers(ViewControllerRegistry registry) { // for (WebMvcConfigurer delegate : this.delegates) { // delegate.addViewControllers(registry); // } } } } -
容器中所有的WebMvcConfigurer都会起一起作用
-
我们自己的配置也会起作用
==效果:SpringMVC的自动配置和我们的扩展配置都会起作用==
c.全面接管SpringMVC
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
.
SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了(可以测试在静态资源路径下的页面是否仍然可以访问?)
我们需要在配置类中添加@EnableWebMvc即可;
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /atguigu 请求来到 success
registry.addViewController("/atguigu").setViewName("success");
}
}
==理解:为什么@EnableWebMvc后SpringBoot的配置都失效了呢==
1)、@EnableWebMvc的核心
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
2)、
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
3)、
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//SpringBoot没有这个WebMvcConfigurationSupport组件其自动配置才会生效 --但是我们 @EnableWebMvc进行了导入 ....
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
4)、@EnableWebMvc将WebMvcConfigurationSupport组件导入进来;
5)、导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能