javascript
tns03505 无法解析名称_SpringBootWeb源码解析SpringMVC自动配置
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 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
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參數時,默認會在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的自動配置
總結
以上是生活随笔為你收集整理的tns03505 无法解析名称_SpringBootWeb源码解析SpringMVC自动配置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sqlmap自动扫描注入点_同天上降魔主
- 下一篇: python解压文件_Python压缩和