前言
说实话SpringBoot用起来真的很简单,但是要想理解透彻,我个人觉得还是件比较困难的事情,不过凡事都得去尝试,就算理解的没有那么的深,但是能有勇气去看,并且找到核心的部分一点点的分析,对于初学者来说,就是一件非常了不起的事情了,大家伙儿说呢?
前端控制器的自动管理
准备好了咱就开始吧,还是老规矩,分析源码之前先想想你要分析哪一块?找到切入点之后,再一步步的查阅
WebMvcAutoConfiguration相信大家应该不陌生了,前面几次源码分析的时候都遇到过它,其实把它当作是SpringBoot的核心也不为过,SpringBoot无非是Spring和SpringMVC的基础上再次做了封装,本质上还是这些东西,但是能把SpringBoot吹的这么神奇的无非就是更加简洁便利的搭建项目!所以说,SpringBoot离不开配置(很多项目启动的配置SpringBoot默认都帮我们配了),而WebMvcAutoConfiguration又是配置的核心所在,所以我们就以WebMvcAutoConfiguration为突破口,来深入SpringBoot!
@Configuration //配置类
@ConditionalOnWebApplication(type = Type.SERVLET) //需要在servlet运行环境下才生效
//如果有Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class这些类才生效
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//缺失WebMvcConfigurationSupport时做的事
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)//自动装配的顺序
//在加载配置的类之后再加载当前类
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = "";
private static final String[] SERVLET_LOCATIONS = { "/" };
}
分析过WebMvcAutoConfiguration 上的注解我们发现在加载WebMvcAutoConfiguration之前还需要加载另外几个类DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class
其中一个还是我们熟悉的 “老朋友” 门卫大叔 DispatcherServlet, 既然跟他比较熟,那么我们先来探望它吧~
首先分析这个方法,大家会发现,在这个方法中,我们new了一个DispatcherServlet对象,并且在@Bean中给这个类取了名字,即为dispatcherServlet
此操作是不是和我们xml文件的这一部分很相似
我们再看这个静态内部类DispatcherServletRegistrationConfiguration,它包含两个属性webMvcProperties以及multipartConfig,前者毋庸置疑是webMvc的一些配置属性,multipartConfig我们之前也接触过,在博文
Marco’s Java【SpringMVC入门(六) 之 SpringMVC的文件上传和下载】 中有详细讲解到,这就是我们的文件上传配置类
其实我们点开webMvcProperties会发现里边有很多配置属性,比如说我们上节才讲到的国际化信息中的Locale类
还有负责处理异步的类,Servlet服务器类,View视图类等等。
另外我们会找到一个非常有意思的配置属性staticPathPattern = "/**"
/**
* Path pattern used for static resources.
*/
private String staticPathPattern = "/**";//静态路径模式路径
为什么说它有意思呢?大家还记得我们之前访问一些静态资源的时候只用加静态资源文件的名称就可以了么?
可能我说的还不够清楚,这样吧,咱们就拿src/main/resources中的static文件来说事儿!虽然我们知道,static文件夹中的文件是不用加前面的文件夹名称就可以直接访问到的,但是大家想过这其中的缘由没?其实答案就这里!
*staticPathPattern 对应了spring.mvc.static-path-pattern这个属性,可以看到默认值是 /**
(代表多层级文件访问路径),例如/resources/**
,它一般表示当你的路径中存在resources/**的时候才会处理请求,这也就解释了为什么springboot在访问静态资源的时候只访问资源啊名称即可。
这里再稍微提一下另外一个容易和它混淆的属性spring.resources.static-locations。
查看源码我们发现spring.resources.static-locations的默认属性是"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"
这是不是和我们之前讲到的静态资源访问路径的优先级别对应上了,默认的文件路径访问优先级别从左到右依次降低。
我们再回到DispatcherServletRegistrationConfiguration这个配置下方的这个方法,发现这个方法无非就只做了一件事情,就是通过通过DispatcherServletRegistrationBean注册DispatcherServlet,后面一章讲解自定义Servelt我们还会碰到这个注册类,其实registration和我们之前在Spring中讲到的的handlerMapping(花名册)的作用比较类似,都是起到将url和方法之间建立映射关系的作用。
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
//创建DispatcherServlet注册器
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
this.webMvcProperties.getServlet().getPath());
//设置servlet-name
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
//设置load-on-startup
registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
//当文件上传配置不为空时,设置文件上传属性
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
这里的操作是不是和xml中的servlet-mapping很相似?
另外这个方法中还有个很有意思的点就是this.webMvcProperties.getServlet().getPath()
方法,点开这个方法我们会发现path是/
,这就意味着所有的请求全部都会进入DispatchServlet前端控制器
讲到这里,再结合之前WebMvcAutoConfiguration讲解的内容(默认SpringBoot内部会加载118个配置),大家应该知道为什么SpringBoot可以自动管理这些配置了吧?
WebMvc的视图解析器分析
WebMvcAutoConfiguration中还有一个静态类叫WebMvcAutoConfigurationAdapter,刚刚我们提到Spring Boot中默认的静态资源配置是将类路径下的/static 、/public、/resources、/META-INF/resources文件夹中的静态资源直接映射为/**。而这个默认行为是在WebMvcAutoConfiguration内部类WebMvcAutoConfigurationAdapter的addResourceHandlers方法中定义的,相关的属性配置类为ResourceProperties、WebMvcProperties,这两个类刚才在上面我们已经提到过了,这里们再来继续研究一下WebMvcProperties中的MVC配置
@Configuration //配置类
@Import(EnableWebMvcConfiguration.class)//启用WebMvcConfiguration
//启用WebMvcProperties和ResourceProperties
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
打开WebMvcProperties之后,引入眼帘的就是我们之前学习过的@ConfigurationProperties(prefix = "spring.mvc")
注解,代表当我们在配置文件(properties或者yml)中使用 “头部(spring.mvc)” 就可以更改SpringBoot中的MVC默认配置。
得知这个信息之后,我们回过头再来看WebMvcAutoConfiguration中的这里关于视图解析器的这一部分代码,不难看出SpringBoot默认帮我们配置了四个视图解析器,分别是InternalResourceViewResolver
(内部资源访问视图解析器),BeanNameViewResolver
(BeanName视图解析器),ContentNegotiatingViewResolver
(内容协商视图解析器)以及LocaleResolver
(国际化信息视图解析器)
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
//创建内部资源访问视图解析器,并注入前缀和后缀
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
//创建BeanName视图解析器
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);//设置视图优先级
return resolver;
}
@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));
//内容协商视图解析器管理另外三个解析器合并为一个视图,因此它应该具有较高的优先级
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
//创建用于国际化信息的视图解析器
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
ContentNegotiatingViewResolver在这几个视图解析器中的地位相当于 “统帅” ,另外三个则是它的得力 “干将”。
InternalResourceViewResolver我们应该比较熟悉了,因为之前配置.jsp页面访问的时候,就是使用此视图解析器配置访问页面的前后缀,当我们通过控制器return到需要跳转的页面时,直接通过页面名称就可以访问到。
在上面我们也分析过,使用 “头部(spring.mvc)” 就可以更改SpringBoot中的MVC默认配置。 因此我们也可以通过上面这种修改访问页面前后缀的方式,改变我们的文件访问路径。但需要注意的是SpringBoot内嵌的tomcat服务器默认是不支持jsp解析的,需要依赖外部tomcat处理。关于这一点后面我们会讲解到。
文件上传及下载的视图解析器
刚刚咱们还提到另外一个属性multipartConfig,其实学习过文件上传的朋友对这里面的属性应该很熟悉了,一次是文件上传路径,最大文件大小,最大请求的大小
在之前的文章里,我们讲解过,如果需要SpringMVC支持文件上传,必须配置CommonsMultipartResolver
同样的,在SpringBoot中也是使用CommonsMultipartResolver通用文件上传视图解析器,只不过对这个视图解析器做了封装,那我们来找找有没有这么一个文件上传的配置类,文件上传的关键字是Multipart
,一般SpringBoot的自动配置类的结尾都是AutoConfiguration
,那我们试着搜索MultipartAutoConfiguration
果不其然!根据源码的内容我们不难看出这个配置类是依赖于CommonsMultipartResolver以及我们刚刚在上面看到的MultipartConfigElement,他还包含一个MultipartProperties文件传配置类,其实到现在咱们可以总结出一个规律,基本上所有的自动化配置类的后缀都是AutoConfiguration,而且所有的配置类上必定有@Configuration
注解,以及一些对指定类或者特定环境的依赖,还有一点就是它们里边基本上都有一个xxxProperties
类,这个类就相当于一个存放配置信息的.properties文件。
正如上图的MultipartProperties,通过下方在yml文件中进行配置(size的单位必须是大写的MB),可以修改它的默认文件属性值。
后语
好啦,本节关于SpringBoot自动管理SpringMVC的源码解析就到此为止啦,后面的章节我们还会有方向的针对SpringBoot的某一个部分进行源码解析,所以大家伙儿有兴趣可以继续阅览哦~