SpringBootWeb源码解析SpringMVC自动配置
yuyutoo 2024-10-27 17:02 2 浏览 0 评论
SpringMVC自动配置
在 Spring Boot 中引入了 spring-boot-starter-web 依赖,并完成了 DispatcherServlet 的自动配置之后,便会通过 WebMvcAutoConfiguration 进行 Spring MVC 的自动配置。
与 DispatcherServletAutoConfiguration 一样,首先会在 spring-boot-autoconfigure 包中的ME TA-INF/spring.factories 配置文件中配置注册类 WebMvcAutoConfiguration,源代码如下。
#自动配置
org. springframework . boot . autoconfigure . EnableAutoConfiguration=\
org. springframework . boot . autoconfigure . web. servlet .WebMvcAutoConfiguratio
n,\
我们直接进入源代码,先看 WebMvcAutoConfiguration 的注解部分。
@Configuration( proxyBeanMethods = false)
@Condit ionalOnWebApplication(type = Type . SERVLET)
@ConditionalOnClass({ Servlet. class, DispatcherServlet.class, WebMvcConfigu
rer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport. class)
@AutoConfigureOrder (Ordered . HIGHEST_ PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration. class,
TaskExecutionAutoConfiguration . class, Validat ionAutoCo
nfiguration.class })
public class WebMvcAutoConfiguration {
。。。
}
WebMvcAutoConfiguration 类的实例化需要满足很多条件,其中就包含必须先完成上节讲到的自动配置 DispatcherServletAutoConfiguration 的初始化。
Spring MVC 在自动配置中的代码较多,官方文档中重点提到了以下功能的实现。定义 ContentNegotiatingViewResolver 和 BeanName ViewResolver 的 Bean。
.对静态资源的支持,包括对 WebJars 的支持。
.自动注册 Converter、 GenericConverter、 Formatter 的 Bean。
.对 HttpMessageConverters 的支持。
.自动注册 MessageCodeResolver.
.对静态 index.html 的支持。
:使用 ConfigurableWebBindingInitializer 的 Bean。
当然,在自动配置类中不只包括了以上的功能实现,还包括其他功能,限于篇幅,这里就不一一-列举 了。下面会挑选几个有代表性的功能进行源代码及实例化过程的分析。
ViewResolver 解析
这里以 ContentNegotiatingViewResolver 和 BeanNameViewResolver 的 bean 的实例化为例进行相应解析。
ContentNegotiatingViewResolver 实例化相关源代码如下。
@Bean
@ConditionalOnBean(ViewResolver . class)
@ConditionalOnMissingBean(name = "viewResolver" ,
value = ContentNegotiatingViewResolver . class)
public ContentNegot iatingViewResolver viewResolver(BeanFactory beanF actory)
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver . setContentNegotiationManager(
beanF actory. getBean(ContentNegot iationManager . class));
resolver. setOrder (Ordered . HIGHEST_ PRECEDENCE);
return resolver;
}
ContentNegotiatingViewResolver 实例化比较简单,创建对象,设置请求资源类型管理器为ContentNegotiationManager, 并 设 置 优 先 级 。 需 要 注 意 的 是 , 要 让ContentNegotiatingViewResolver 正 常 工 作 , 需要设置更高的优先级 ( 默认为Ordered.HIGHEST_ PRECEDENCE)。
ContentNegotiatingViewResolver 类实现了 ViewResolver,但它并不直接解析视图,而是委托给其他解析器来完成。默认情况,它是从 Spring 上下文查找视图解析器,并调用这些解析 器 。 也 可 以 在 初 始 化 该 类 时 通 过 setViewResolvers 方 法 设 置 解 析 器 属 性(viewResolvers) 。在此,默认的实例化操作中并没有对 SetViewResolvers 方法进行设置。
BeanNameViewResolver 实例化相关源码如下。
@Bean
@ConditionalOnBean(View. class)
@Conditiona lOnMissingBeanpublic BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver . setOrder(Ordered. LOWEST_ PRECEDENCE- 10);
return resolver;
}
BeanNameViewResolver 主要通过逻辑视图名称匹配定义好的视图 Bean 对象。一般情况下,对应的 Bean 对象需要注册到 Spring 的上下文中,BeanNameViewResolver 会返回名称匹配的视图对象。
BeanNameViewResolver 实例化的前提条件是容器中 View 实现类的 Bean 存在。
BeanNameViewResolver 的部分源码如下。
public class BeanNameViewResolver extends WebApplicationObjectSupport imple
ments ViewResolver, Ordered {
//实现 Ordered 接口,支持对 ViewResolver 排序, 值越小优先级越高
private int order = Ordered. LOWEST_ PRECEDENCE;
@Override
@Nullable
public View resolveVi ewName (String viewName, Locale locale) throws Beans -
Exception
//获取上下文
ApplicationContext context = obtainApplicationContext();
//查找上下文中是否有"viewName”的 Bean 定义
if (!context . containsBean(viewName)) {
return null;
}
//判断"viewName”的 bean 对象是否是 View 类型
if (!context. isTypeMatch(viewName, View. class)) {
if (logger . isDebugEnabled()) {
logger. debug("Found bean named '”+ viewName +”' but it does not i
mplement View");
return null;
}
返回上下文中指定名称的 View 类型的 Bean
return context . getBean(viewName, View. class);
}
BeanNameViewResolver 的 resolveViewName 方法首先通过名称判断对应视图是否存在,当通过名称无法匹配时,会通过类型进行视图判断,如果存在对应的 Bean,则获取对应的View 对象并返回。
静态资源的支持
前端页面往往需要访问到静态资源,SpringBoot 对静态资源(比如图片、CSS、JS 等)的支持 , 也 包 括 对 webjars 的 支 持 , 主 要 是 通 过 实 现 接 口 WebMvcConfigurer 的addResource-Handlers 方法来完成的。
WebMvcConfigurer 的接口实现类为 WebMvcAutoConfiguration 的内部类,这样设计的主要目的是确保 WebMvcConfigurer 不在类路径中时不会读取 WebMvcConfigurer 的实现类。
这里的内部实现类为 WebMvcAutoConfigurationAdapter。
而我们要讲的对静态资源的支持便是通过 WebMvcAutoConfigurationAdapter 实现接口WebMvcConfigurer 的 addResourceHandlers 方法来完成的。
@Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
//如果默认资源处理器为不可用状态则返回
if (!this . resourceProperties. isAddMappings()) {
logger . debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this . resourceProperties . getCache()- getPeriod();
CacheControl cacheControl = this. resourceProperties . getCache( )
. getCachecontrol() . toHttpCacheControl();
//针对 webjars 做了特殊的判断处理
if (!registry . hasMappingForPattern(" /webjars/**")) {
//如果不存在针对 webjars 的配置, 则在此处添加,并没置默认路径等
customizeResourceHandlerRegistrat ion(registry
. addResourceHandler(" /webjars/**")
. addResourceLocations("classpath:/
META-INF/resources/webjars/")
. setCachePeriod(getSeconds (cachePe
riod))
. setCacheControl(cacheControl));
}
String staticPathPattern = this . mvcProperties . getStaticPathPattern();
//如果当前的 ResourceHandlerRegistry 里面资源映射没有"/**",则启用默认的静态资源处
理if (!registry. hasMappingForPattern(staticPathPattern)) {
customi zeResourceHandlerRegistration(
registry . addResourceHandler(staticPathPattern)
. addResourceLocations (getResourceLocations(
this. resourceProperties . getStaticLocations()))
. setCachePeriod(getSeconds( cachePeriod))
. setCacheControl(cacheControl));
}
}
以上代码中重点进行了 webjars 资源路径和静态资源路径等默认值的初始化。首先,如果判断当前 ResourceHandlerRegistry 中不存 在“/webjars/**”,则设置 webjars 的资源路径和缓存 配 置 为 默 认 值 ; 其 次 , 判 断 当 前 ResourceHandlerRegistry 是 否 存 在“/**”(getStaticPathPattern 方 法获得的默认值)映射,如果不存在,则使用默认的映射路径、资源路径和缓存配置。
默 认 的 静态 资 源 映 射 路 径 在 ResourceProperties 类 中 定 义, 在 上 面 的 代 码 中是 由resourceProperties 的 getStaticLocations()方法获得。
ResourceProperties 中默认路径定义相关代码如下。
@ConfigurationProperties(prefix = "spring . resources", ignoreUnknownFields =
false)
public class ResourceProperties{
private static final String[] CLASSPATH RESOURCE_ LOCATIONS = { "classpat
h: /META- INF/resources/",
"classpat
h:/resources/", "classpath:/static/", "classpath:/public/" };
private String[] staticLocations = CLASSPATH RESOURCE LOCATIONS;
至此我们可以看出,Spring Boot 默认会加载 classpath:/META-
INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/路径下的静态资源。这是“约定”的一部分,也是为什么我们在实践中默认会将静态资源都放置在以上路径下。
静态 index.html
当 Spring Boot 的 web 项目启动时,会寻找默认的欢迎页面。下面我们来当 Spring Boot 的web 项目启动时,会寻找默认的欢迎页面。下面我们来看 Spring Boot 默认对静态 index.html的支持是如何实现的。该功能是在内部类 EnableWebMvcConfiguration 中通过 WelcomePageHandlerMapping来实现的。主要用来查找默认路径下的 index.html (或 index 模板)页面,并展示默认的欢迎页面,代码如下。
@Bean
public We lcomePageHandlerMapping welcomePageHandlerMapping(ApplicationConte
xt applicationContext ,
FormattingConver
sionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvide
r) {
//构造 welcomePageHandLerMapping 对象
WelcomePageHandlerMapping
we
lcomePageHandlerMapping
=
new
WelcomePageHand
ler-
Mapping(
new TemplateAvailabilityProviders (applicationContext), applicationConte
xt,
getWelcomePage(),
this . mvcProperties . getStaticPathPattern());
//设置拦截器
welcomePageHandlerMapping . setInterceptors(getInterceptors (mvcConversionSe
rvice, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
//获取默认查找 index. html 的路径数组
static String[] getResourceLocations (String[] staticLocations) {
String[] locations = new String[staticLocations . length
SERVLET_ _LOCATIONS. length];
System. arraycopy(staticLocations, 0, locations, 0, staticLocations. lengt
h);
System. arraycopy(SERVLET_ LOCATIONS, 0, locations, staticLocat ions . length,
SERVLET_ LOCATIONS. length);
return locations;
}
//遍历资源路径并拼接每个路径下的 index. htmL 文件,过德出可用的 index. htmL 文件
private Optional<Resource> getwelcomePage() {
String[] locations = getResourceLocations (
this . resourceProperties . getStaticLocations());//转换并筛选出符合条件的第一个
return Arrays . stream(locations ) . map(this: :getIndexHtml)
. filter(this: :isReadable). findFirst();
}
//获取欢迎页资源的名称:路经+ index. html
private Resource getIndexHtml (String location) {
return this . resourceLoader . getResource(location + "index. html");
}
关于以上代码,我们首先看 WelcomePageHandlerMapping 类, 该类本身就是为欢迎页面量身定做的,实现了抽象类 AbstractUrlHandlerMapping。该类的构造方法接收以下 4 个参数。
-TemplateAvailabilityProviders: TemplateAvailabilityProvider 的 Bean 的集合,可用于检查哪 些 ( 如 果 有 ) 模 板 引 擎 支 持 给 定 的 视 图 。 默 认 支 持 缓 存 响 应 , 除 非 将spring.template .provider.cache 属性设置为 false。
:ApplicationContext:为应用程序提供配置的控制接口。在应用程序运行时,它是只读的,但是如果实现类支持,则可以重新加载。
.Optional : index.html 对 应的 Resource,主要通过上述代码中的 getWelcome-Page 方法获得。
:String staticPathPattern:静态资源路径表达式,默认为“/**”,值定义于 WebMvc- Properties中。
我们再简单看一下 WelcomePageHandlerMapping 类构造方法中的业务逻辑处理源码。
final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabil
ityProviders,
ApplicationContext applicationContext, Optional
<Resource> welcomePage,
String staticPathPattern) {
if (welcomePage . isPresent() && "/**" . equals(staticPathPattern)) {
logger . info("Adding welcome page: ”+ welcomePage .get());
setRootVi ewName("forward: index . html");
} else if (welcomeTemplateExists (templateAvailabilityProviders, applica
tion-
Context)) {
logger. info("Adding welcome page template: index");
setRootViewName(" index") ;
}
}
}
WelcomePageHandlerMapping的构造方法中处理了两个分支判断:当index.html资源存在,并且静态资源路径为“**”时,设置 RootView 的名称为“forward:index.html"。也就是说会跳转到 index.html 页面。如果不满足上述情况,再判断是否存在欢迎模板页面,如果存在,则设置 RootView 为 index。
另外,在获取 WelcomePageHandlerMapping 的 Optional<Resource>参数时,默认会在classpath:/META-INF/resources/、classpath:/resources/.classpath:/static、classpath:/public/路径 下去寻找 index.htmI 作为欢迎页面。
这些路径的定义同样位于上节提到的 ResourceProperties 类中。如果有多个 index.html 文件存在于以上路径中,它们的优先级按照上面路径的顺序从高到低排列。
关于 Spring MVC 配置的相关内容较多,以上只是针对在官方文档中提到的一些典型功能的代码实现和原理进行讲解。在学习 Spring MVC 相关自动配置时,把握住一个核心思路即可:
对照没有使用 Spring Boot 的场景,我们集成 MVC 需要进行哪些配置、涉 及哪些类,而Spring Boot 又是如何将其自动配置的。
本文给大家讲解的内容是SpringBootWeb应用源码解析:SpringMVC的自动配置
- 下篇文章给大家讲解的是SpringBootWeb应用源码解析:综合实战;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
相关推荐
- MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库
-
前言:本文章是在同处局域网内的两台windows电脑,且MySQL是5.5以上版本下进行的一主多从同步配置,并且使用的是集成环境工具PHPStudy为例。最后就是ThinkPHP5的分布式的连接,读写...
- thinkphp5多语言怎么切换(thinkphp5.1视频教程)
-
thinkphp5多语言进行切换的步骤:第一步,在配置文件中开启多语言配置。第二步,创建多语言目录。相关推荐:《ThinkPHP教程》第三步,编写语言包。视图代码:控制器代码:效果如下:以上就是thi...
- 基于 ThinkPHP5 + Bootstrap 的后台开发框架 FastAdmin
-
FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。主要特性基于Auth验证的权限管理系统支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置支持单...
- Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示
-
本文实例讲述了Thinkphp5.0框架实现控制器向视图view赋值及视图view取值操作。分享给大家供大家参考,具体如下:Thinkphp5.0控制器向视图view的赋值方式一(使用fetch()方...
- thinkphp5实现简单评论回复功能(php评论回复功能源码下载)
-
由于之前写评论回复都是使用第三方插件:畅言所以也就没什么动手,现在证号在开发一个小的项目,所以就自己动手写评论回复,没写过还真不知道评论回复功能听着简单,但仔细研究起来却无法自拔,由于用户量少,所以...
- ThinkPHP框架——实现定时任务,定时更新、清理数据
-
大家好,我是小蜗牛,今天给大家分享一下,如何用ThinkPHP5.1.*版本实现定时任务,例如凌晨12点更新数据、每隔10秒检测过期会员、每隔几分钟发送请求保证ip的活性等本次分享,主要用到一个名为E...
- BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统
-
BeyongCms内容管理系统(简称BeyongCms)BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统,适用于企业Cms,个人站长等,针对移动App、小程序优化;提供完善简...
- YimaoAdminv3企业建站系统,使用 thinkphp5.1.27 + mysql 开发
-
介绍YimaoAdminv3.0.0企业建站系统,使用thinkphp5.1.27+mysql开发。php要求5.6以上版本,推荐使用5.6,7.0,7.1,扩展(curl,...
- ThinkAdmin-V5开发笔记(thinkpad做开发)
-
前言为了快速开发一款小程序管理后台,在众多的php开源后台中,最终选择了基于thinkphp5的,轻量级的thinkadmin系统,进行二次开发。该系统支持php7。文档地址ThinkAdmin-V5...
- thinkphp5.0.9预处理导致的sql注入复现与详细分析
-
复现先搭建thinkphp5.0.9环境...
- thinkphp5出现500错误怎么办(thinkphp页面错误)
-
thinkphp5出现500错误,如下图所示:相关推荐:《ThinkPHP教程》require():open_basedirrestrictionineffect.File(/home/ww...
- Thinkphp5.0极速搭建restful风格接口层
-
下面是基于ThinkPHPV5.0RC4框架,以restful风格完成的新闻查询(get)、新闻增加(post)、新闻修改(put)、新闻删除(delete)等server接口层。1、下载Thin...
- 基于ThinkPHP5.1.34 LTS开发的快速开发框架DolphinPHP
-
DophinPHP(海豚PHP)是一个基于ThinkPHP5.1.34LTS开发的一套开源PHP快速开发框架,DophinPHP秉承极简、极速、极致的开发理念,为开发集成了基于数据-角色的权限管理机...
- ThinkPHP5.*远程代码执行高危漏洞手工与升级修复解决方法
-
漏洞描述由于ThinkPHP5框架对控制器名没有进行足够的安全检测,导致在没有开启强制路由的情况下,黑客构造特定的请求,可直接GetWebShell。漏洞评级严重影响版本ThinkPHP5.0系列...
- Thinkphp5代码执行学习(thinkphp 教程)
-
Thinkphp5代码执行学习缓存类RCE版本5.0.0<=ThinkPHP5<=5.0.10Tp框架搭建环境搭建测试payload...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库
- thinkphp5多语言怎么切换(thinkphp5.1视频教程)
- 基于 ThinkPHP5 + Bootstrap 的后台开发框架 FastAdmin
- Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示
- thinkphp5实现简单评论回复功能(php评论回复功能源码下载)
- ThinkPHP框架——实现定时任务,定时更新、清理数据
- BeyongCms系统基于ThinkPHP5.1框架的轻量级内容管理系统
- YimaoAdminv3企业建站系统,使用 thinkphp5.1.27 + mysql 开发
- ThinkAdmin-V5开发笔记(thinkpad做开发)
- thinkphp5.0.9预处理导致的sql注入复现与详细分析
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)