SpringBoot学习笔记—Day06
SpringMVC的自动配置
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何 渲染(转发?重定向?))
给容器中添加视图解析器的方法
@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;
}
视图解析器解析视图的方法
@Override
@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 = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
List<View> 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;
}
}
List< View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);在这行中是获取候选的视图对象
接下来View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);这行是选择一个最适合的视图对象,最后将视图对象返回
怎样选择候选的视图对象
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");
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
candidateViews.add(view);
}
for (MediaType requestedMediaType : requestedMediaTypes) {
List<String> 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;
}
其中可以看到for循环,遍历所有的视图解析器,
for (MediaType requestedMediaType : requestedMediaTypes) {
List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
for (String extension : extensions) {
String viewNameWithExtension = viewName + '.' + extension;
view = viewResolver.resolveViewName(viewNameWithExtension, locale);
if (view != null) {
candidateViews.add(view);
}
}
}
ContentNegotiatingViewResolver:组合所有的视图解析器的;
如何定制:我们可以自己给容器中添加一个视图解析器;ContentNegotiatingViewResolver会自动的将其组合进来;
Support for serving static resources, including support for WebJars (see below).静态资源文件夹路 径,webjars
Static index.html support. 静态首页访问
Custom Favicon support (see below). favicon.ico
自动注册了 of Converter , GenericConverter , Formatter beans.
Converter:转换器; public String hello(User user):类型转换使用Converter
Formatter 格式化器; 2017.12.17===Date;
@Bean @ConditionalOnProperty(prefix = "spring.mvc", name = "date‐format")//在文件中配置日期格 式化的规则
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
}
HttpMessageConverters是SpringMVC用来转换Http请求和响应的
public HttpMessageConverters(HttpMessageConverter<?>... additionalConverters) {
this(Arrays.asList(additionalConverters));
}
这个是HttpMessageConverters的有参构造方法,参数就是所有的HttpMessageConverter,容器会自动初始化HttpMessageConverters,也就是说会自动的将所有的HttpMessageConverter获取到,(这里我是这样理解的),它的构造方法全部都是有参的,如果我们要自定义一个HttpMessageConverter,那么只需要将我们自定义的HttpMessageConverter加入到容器中即可
@Bean
public HttpMessageConverters myHttpMessageConverters(){
HttpMessageConverter<?> additional = ...;
HttpMessageConverter<?> another = ...;
return new HttpMessageConverters(additional,another);
}
如何修改SpringBoot的自动配置
模式
1)SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的,如果有,就用用户自己配置的,如果没有,就自动配置,如果有些组件是可以有多个的,那么SpringBoot会将用户配置的和自己默认配置的组合起来(一般来讲就是加入到集合中)
2)如果我们想保持SpringBoot对SpringMVC的控制,而且只想要添加一部分功能,比如viewController,或者interceptors功能,我们可以添加我们自己配置的Configuration类
WebMvcAutoConfiguration是SpringMVC的自动配置类
@Import(EnableWebMvcConfiguration.class),这里加入了EnableWebMvcConfiguration这个类
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
}
从他的父类中找到
@Configuration(proxyBeanMethods = false)
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);
}
}
在这个类中有许多的添加SpringMvc的配置的方法,比如addViewControllers方法
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
在这里可以看到,是拿到所有的WebMvcConfigurer,将每一个都加入到集合中(也就是这些WebMvcConfigurer都会起作用)
@Override
public void addViewControllers(ViewControllerRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addViewControllers(registry);
}
}
WebMvcConfigurerAdapter类实现的WebMvcConfigurer接口,所以我们自己写的配置类只需要继承WebMvcConfigurerAdapter类就可以了,但是Spring 5.0后,WebMvcConfigurerAdapter被废弃,取代的方法有两种:
①直接实现implements WebMvcConfigurer(官方推荐)
之前因为实现这个接口需要实现接口中的方法,太麻烦,才创建的WebMvcConfigurerAdapter实现类,之后java8中加入了default关键字,所以WebMvcConfigurer接口中的所有方法全部都是default修饰的,所以我们直接实现该接口不必再去实现接口中的所有方法,只需要实现我们想要实现的方法即可,不会影响到Spring Boot自身的@EnableAutoConfiguration
②extends WebMvcConfigurationSupport
而使用第二种方法相当于覆盖了@EnableAutoConfiguration里的所有方法,每个方法都需要重写,比如,若不实现方法addResourceHandlers(),则会导致静态资源无法访问,实现的方法如下:
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/META-INF/resources/")
.addResourceLocations("classpath:/resources/")
.addResourceLocations("classpath:/static/")
.addResourceLocations("classpath:/public/");
super.addResourceHandlers(registry);
}
上面的方法是给springMVC加配置,也就是说不改变原来SpringMVC的自动配置
全面接管SpringMVC
SpringBoot对SpringMVC的自动配置不需要了,所有都是自己配置,所以SpringMVC的功能都失效了
在配置类上添加@EnableWebMvc注解即可
或者使用上述的第二种方式也可以
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
此注解通过@Import注解加载了一个配置类DelegatingWebMvcConfiguration,这个类继承了WebMvcConfigurationSupport类,故继承WebMvcConfigurationSupport类,重写里面的方法,或者添加
@EnableWebMvc注解作用是相同的
为什么加入@EnableWebMvc注解,SpringMVC注解的作用就失效了
先看WebMvcAutoConfiguration源码
@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)这里的意思是,容器中如果缺少WebMvcConfigurationSupport.class这个组件的话判断为true,否则判断为false,Conditrional注解判断为false的话,其修饰的配置类也会失效,所以我们看@EnableWebMvc注解源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
在@Import(DelegatingWebMvcConfiguration.class)这里引入 DelegatingWebMvcConfiguration.class,而DelegatingWebMvcConfiguration.class继承了WebMvcConfigurationSupport类,所以我们将@EnableWebMvc注解添加到某个配置类上时,会导致容器中多了WebMvcConfigurationSupport类这个组件,进而导致在WebMvcAutoConfiguration类上的@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)注解判断为false,所以WebMvcAutoConfiguration这个配置类失效,所以SpringMVC自动配置的功能全部失效
本文深入探讨SpringBoot对SpringMVC的自动配置机制,包括视图解析器的自动配置、静态资源处理、日期格式化及HTTP消息转换器的配置。同时,讲解了如何定制自动配置,以及如何通过添加自定义配置类或使用@EnableWebMvc完全接管SpringMVC。
2680

被折叠的 条评论
为什么被折叠?



