天天看點

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

上一篇我們講解了Spring Boot的自動配置的機制以及原理,本篇我們來介紹使用Spring Boot進行Web開發時,Spring Boot是如何為我們提供強大的Web開發依賴的支援的。

一、Web開發環境自動配置

上一篇講到Spring Boot的自動配置環節,提到大部分的常用開發架構Spring Boot都幫我們做好了自動配置,而Web開發也不例外。在Spring Boot中,Web開發的自動配置類為:org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration。

我們在樣例工程的依賴中找到WebMvcAutoConfiguration的依賴:

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

然後在下面的 包下,可以找到WebMvcAutoConfiguration類:

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

我們打開WebMvcAutoConfiguration類的源碼,可以看到該類上面的注解:

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    //......
}
           

其中我們可以看到“@ConditionalOnClass”注解,這個就是我們前面提到的條件注解,隻要滿足後面的class存在的條件,該類才會加載生效。這裡就是需要擁有JavaWeb的“Servlet”類,Spring MVC的“DispatcherServlet”類以及“WebMvcConfigurerAdapter”擴充卡類,才會執行個體化該WebMvcAutoConfiguration的Web環境自動配置類。

然後我們再來看源碼中的httpPutFormContentFilter()方法:

@Bean
@ConditionalOnMissingBean(HttpPutFormContentFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)
public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
    return new OrderedHttpPutFormContentFilter();
}
           

其中包含一個“@ConditionalOnMissingBean”的注解,即是如我們沒有自己配置HttpPutFormContentFilter攔截器的話,才會幫我們自動配置該攔截器。

而在該類中還有大量的這種配置,感興趣的同學可以詳細的讀取其源碼。下面我們重點來看有關視圖解析器的自動配置源碼:

@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
public BeanNameViewResolver beanNameViewResolver() {
    BeanNameViewResolver resolver = new BeanNameViewResolver();
    resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
    return resolver;
}
           

defaultViewResolver是當沒有配置視圖解析器ViewResolver時(視圖解析器幫助我們渲染與使用者的互動視圖,如JSP頁面),用來加載預設的視圖解析器的配置方法。我們可以看到,在defaultViewResolver方法上面有注解“@Bean”,說明該方法傳回的類會作為一個Spring對象被執行個體化,加載至Spring容器中。而該方法傳回的類為“InternalResourceViewResolver”,這和我們在傳統的Spring MVC開發中在Spring配置檔案中配置的:

<!-- 内部資源視圖解析器-->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages"/>
    <property name="suffix" value=".jsp"/>
</bean>
           

效果是一樣的。而Spring Boot在我們沒有配置視圖解析器的時候,就幫我們自動執行個體化“InternalResourceViewResolver”為我們的視圖解析器。

而其中的視圖字首(prefix)和視圖字尾(suffix)是從哪裡自動擷取的?從代碼中我們可以看到是從一個“mvcProperties”對象中取出的,而該對象的執行個體化包括配置的前字尾的值,使我們在搭建Spring Boot環境時,在全局配置檔案application.properties中配置的:

spring.mvc.view.prefix= # Spring MVC view prefix.
spring.mvc.view.suffix= # Spring MVC view suffix.
           

二、自動配置靜态資源

我們在之前的Spring Boot樣例工程中配置全局配置檔案application.properties:

server.port=8088
server.servlet-path=/

logging.level.org.springframework=DEBUG
           

然後在src/main/webapp和src/main/resources/images下分别放置一張圖檔:

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

然後啟動工程,在浏覽器中通路該圖檔1(http://localhost:8088/1.png):

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

可以看到圖檔被加載出來,而通路圖檔2(http://localhost:8088/images/3.png):

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

則不會被加載出來。這是為什麼呢?

其實原因很簡單,就是我們沒有為靜态資源配置加載路徑,導緻圖檔2加載不出來。而圖檔1能加載出來是因為預設的web開發中會預設配置webapp目錄的靜态資源可直接加載。

那麼,如何配置Spring Boot使得其可以加載除了webapp目錄以外的指定的靜态資源呢?

其實當我們沒有配置Spring MVC的靜态資源加載路徑時,Spring Boot會幫我們配置為“/**”,即相當于在application.properties中配置以下資訊:

spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpth:/public/
           

此時靜态資源的所有通路映射到以下目錄:

classpath:/static

classpath:/public

classpath:/resources

classpath:/META-INF/resources

Spring Boot 預設會挨個從/src/main/resources/目錄下的resources、static、public裡面找是否存在相應的資源,如果有則直接傳回。優先級順序為:META-INF/resources > resources > static > public。

這點我們也可以通過源碼證明。我們可以檢視spring-boot-autoconfigurejar包下的org.springframework.boot.autoconfigure.web包下的ResourceProperties類:

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {

    private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };

    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
            "classpath:/META-INF/resources/", "classpath:/resources/",
            "classpath:/static/", "classpath:/public/" };

    private static final String[] RESOURCE_LOCATIONS;

    static {
        RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
                + SERVLET_RESOURCE_LOCATIONS.length];
        System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
                SERVLET_RESOURCE_LOCATIONS.length);
        System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
                SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
    }

    /**
     * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
     * /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
     */
    private String[] staticLocations = RESOURCE_LOCATIONS;
    //下面代碼省略
}
           

可以看到Spring Boot的自動配置中,預設指定了staticLocations的相關值。

而其實Spring Boot做的事情就是幫Spring MVC的ResourceHttpRequestHandler的ResourceResovlers設定了兩個參數:

(1)靜态資源請求響應路徑的配置(mapping)

(2)靜态資源根目錄的指定(location)

這個在傳統Spring MVC的配置檔案中是這麼配置的:

<mvc:resources location="/image" mapping="/**"/>
           

即spring.mvc.static-path-pattern和spring.resources.static-locations給Spring MVC中ResourceHttpRequestHandler的ResourceResovlers設定了resolveUrlPath和locations。

也就是說,我們剛剛如果在/src/main/resources/目錄下的下面建立META-INF/resources、resources、static、public檔案夾,在下面放檔案,也是可以直接通路的。

我們這裡不修改任何配置檔案,在在/src/main/resources/目錄下的下面建立META-INF/resources、resources、static、public檔案夾,在下面分别放一張圖檔:

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

重新開機應用通路圖檔3/4/5/6:

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)
【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)
【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)
【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

發現全部可以正常通路。說明Spring Boot為我們預設配置的就是這些。當我們需要通路images下的圖檔2的時候,可以在spring.resources.static-locations的最後配置images的路徑:

spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpth:/public/,classpth:/images/
           

然後重新開機應用,發現可以通路了:

【Spring Boot學習總結】9.Web開發-自動配置與靜态資源配置(源碼分析)

如果我們想統一指定靜态資源的url請求路徑請求規則,就指定前面提到的那個spring.mvc.static-path-pattern配置,此時Spring Boot會在加載Spring MVC配置時指定

靜态資源請求響應的路徑。

我們這裡配置為:

spring.mvc.static-path-pattern=/static/**
           

也就是當我們在浏覽器中輸入“http://localhost:8080/static/”後直接跟靜态資源的名稱就可以通路(如http://localhost:8080/static/1.png),不加的話則不會比對任何資源,這就是為了統一通路資源規範。

當然除了在上面的application.properties中配置以外,也可以建立配置類,繼承WebMvcConfigurerAdapter類,并重寫addResourceHandlers方法,為其指定ResourceHandler以及ResourceLocations即可:

import org.springframework.context.annotation.Configuration; 
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 
@Configuration 
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/images/");
        super.addResourceHandlers(registry);
    }
}
           

參考:

傳智播客《Spring Boot實戰與原理分析》視訊課程

(https://pan.baidu.com/s/1o9M2bGI 密碼:jxg8)

轉載請注明出處:https://blog.csdn.net/acmman/article/details/82051343

繼續閱讀