百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

SpringBootWeb源码解析SpringMVC自动配置

yuyutoo 2024-10-27 17:02 4 浏览 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的自动配置

  1. 下篇文章给大家讲解的是SpringBootWeb应用源码解析:综合实战;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

相关推荐

网站制作的流程是什么呢?简单大概的流程

关注我!了解更多网站建设的小干货~如今,随着网络时代的全面到来,网站在人们的生活和工作中发挥着极其重要的作用。网站制作的发展使更多的人加入了这个行业。如果你想掌握网站制作的知识,你可以在学校或网上学习...

一款谷歌(Google)打造的广告网页设计制作软件

GoogleWebDesigner是由谷歌(Google)打造的一款广告网页设计制作软件,它能够帮助从事于广告网页设计工作或是有这方面需求的用户更加有效快速的进行完成相关的行业设计工作,软件可以支...

普通网站如何制作一个网站?

对行外人来讲,在预备做一个网站项目时,最想了解的无非就是网站制作的悉数流程。网站制作是要有计划的,事先策划好才能更快更好的完成。网站的几个基本组成元素:域名+空间+程序+模板+维护经验+日常管理.网站...

用纯Python就能写一个漂亮的网页,再见HTML

再见HTML!用纯Python就能写一个漂亮的网页我们在写一个网站或者一个网页界面的时候,需要学习很多东西,对小白来说很困难!比如我要做一个简单的网页交互:天啊,听听头都大呢!其实我就给老板做一个...

HTML表单4(form的action、method属性)——零基础自学网页制作

表单的工作过程表单的信息发送与处理过程可以简单的进行图示,如下图。以注册会员为例,用户在自己的电脑上打开相应的注册表单页面填写信息,完成填写后点击提交按钮,也就是图中1所示过程。这时浏览器会将这些信息...

官网网站设计网页制作模板建站前端自适应响应式网站仿站门户

案例背景航科慧联无人机搜索雷达能够在多种天气下检测到无人机的入侵、并获得目标的距离、方向和高度等具体信息,是无人机反制作战中的关键设备。航科慧联无人机搜索雷达能够在多种天气下检测到无人机的入侵、并获得...

软网推荐:在线制作软件图标

在制作PPT演示、软件、网页或其他程序时,我们往往需要用到一些个性化的图标。现在,即便是不安装任何软件,也可以上网在线制作自己需要的图标。首先访问如下制作网址:http://www.rw-design...

自定义跳转的h5网页如何制作?

文章来源:墨鹊微站...

网页如何制作?这几点要知道

这是一个个性张扬的时代,也是一个动手能力和动脑能力都比较强的时代,因此很多人对于能够自己动手完成的东西,都不太想假手于人。于是网页制作成了各大搜索引擎里面排名比较靠前的关键词之一。想要知道网页如何制作...

手机端网站简单制作教程,怎么快速制作一个移动端的网站

想要创建一个手机端的网站,需要有域名、已经完成网站页面的开发设计,零基础朋友不懂代码技术,直接在线套用乔拓云里面的网站模板来开发是比较简单可行的,进入乔拓云网,复制网站模板编辑网站的内容,注册域名后绑...

几张动图教你轻松了解Dreamweaver做网页

施老师:当今可是互联网时代,人们的生活、社交离不开互联网,那么不管你是网页设计师,还是销售达人,还是个体户,总必不可少的要在网上呈现一些页面给客户看,这个就是让你做网页,而Dreamweaver是做网...

用Deepseek制作网页版的汉诺塔游戏保姆级教程

在deepseek中输入:“帮我做一个网页版的汉诺塔演示游戏,游戏包含2层、3层、4层、5层的汉诺塔游戏演示,制作自动求解演示按钮,点击按钮就可以生成出步数,同时自动演示最优解动画。”...

JS制作网页版计算器

大家晚上好,我是洁哥,抱歉今天有点晚了,但是洁哥不会缺席哦,今天我们来看一个JS实现网页版计算器的例题,先来看一看出来的效果吧(123+123=246)(123-123=0)(123*123=1512...

网页制作流程哪几步

在数字化时代,网页制作成为企业和个人展示形象、传递信息的重要方式。但是,许多人对于网页制作的流程仍感到困扰。为了解决这一问题,我们将深入探讨网页制作的关键步骤,助您更好地理解和应用这一过程。第一步:需...

这4个设计技巧,教你做好个人网页制作

随着互联网发展,个人建站已经不是什么稀奇事,学生、求职者、插画师、摄影师、作家……都可以制作个人网站,用来展示自身形象,或者吸引粉丝。那么如何做好个人网站呢?在不懂设计和技术知识的情况下,个人网页制作...

取消回复欢迎 发表评论: