Spring Boot 學習(3)

文 by / 林本託html

Tips
作一個終身學習的人。git

Spring Boot

Tips
代碼路徑:https://github.com/iqcz/Springbootdemo/tree/master/code01/ch3github

Web 框架行爲調整

在此章節中,主要包括以下內容:web

  • 配置路由匹配模式;
  • 配置自定義靜態路徑映射;
  • 經過EmbeddedServletContainerCustomizer調優Tomcat;
  • 選擇嵌入式servlet容器;
  • 添加自定義鏈接。

一. 配置路由匹配模式

當咱們構建Web應用程序時,並不老是使用一些默認的配置。 有時,咱們要建立包含字符「.」的 RESTful 風格的 URL。「.」字符在Spring做爲分隔符定義格式,例如path.xml中的點,或者咱們可能不想識別路徑尾部的斜槓,如/home/等。 Spring爲咱們提供了對這些問題提供了一種輕鬆的實現。spring

在前面的第二節中,咱們介紹了WebConfiguration類,它繼承了WebMvcConfigurerAdapter類。 經過繼承,能夠重寫面向過濾器,格式化,還有其餘格式的方法。 一樣,還能夠重寫配置路徑匹配的方法。apache

假設ISBN格式容許使用點來將圖書編號與修訂版本分開,看起來像「[isbn-number].[revision]」的格式。編程

咱們將配置咱們的應用程序不使用「.*」的後綴模式匹配,而且在解析參數時不忽略點以後的值。 咱們執行如下步驟:瀏覽器

首先,須要在WebConfiguration類中,加入以下內容:緩存

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.setUseSuffixPatternMatch(false).
      setUseTrailingSlashMatch(true);
}

同時,須要引入import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;類。tomcat

而後,執行./gradlew clean bootRun命令。

在瀏覽器中,輸入 http://localhost:8080/books/978-1-78528-415-1.1 , 而後會看到以下結果:

isbn-number.revision

若是輸入正確的 ISBN,會看到以下的結果:

ISBN

咱們來看看具體作了什麼事情。 configurePathMatch(PathMatchConfigurer configurer)方法有能力設置咱們本身的行爲,但願Spring如何將請求URL路徑與控制器參數相匹配:

  • configurer.setUseSuffixPatternMatch(false)方法表示不想使用「.*」後綴,不忽略最後一個點號後面的字符。這轉換爲Spring解析整個978-1-78528-415-1.1做爲BookController的{isbn}參數。因此, 「http://localhost:8080/books/978-1-78528-415-1.1」 和 http://localhost:8080/books/978-1-78528-415-1」 是兩個不一樣的 URL。

  • configurer.setUseTrailingSlashMatch(true)方法表示咱們想使用「/」在URL中做爲匹配,即便 URL 中不存在「/」。 因此,「http://localhost:8080/books/978-1-78528-415-1」 和 「http://localhost:8080/books/978-1-78528-415-1/」 效果是同樣的。

若是要進一步配置路徑匹配的方式,能夠提供本身的PathMatcherUrlPathHelper實現,但這些在最極端和自定義的狀況下都是必需的,通常狀況下不推薦使用。

二. 配置自定義靜態路徑映射

在前面的內容中,咱們講解了如何調整請求的URL路徑映射,並將其轉換爲控制器方法。 除此而外,還能夠控制、Web應用程序處理靜態文件,這些文件可能存在與文件系統中,或可部署的歸檔文件中。

假設咱們想經過應用程序的 http://localhost:8080/internal/application.properties 的靜態網址公開咱們的內部application.properties 文件。 要開始執行此操做,請繼續執行下面的步驟。

首先,在WebConfiguration類中,重寫addResourceHandlers方法:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
  registry.addResourceHandler("/internal/**")
    .addResourceLocations("classpath:/");
}

執行./gradlew clean bootRun。服務啓動之後,在瀏覽器中輸入:http://localhost:8080/internal/application.properties,在個人環境裏,系統是 macOS,瀏覽器是 Chrome,會下載application.properties文件,可能會根據每一個人的操做系統和瀏覽器的不一樣,行爲會不同。

咱們重寫的addResourceHandlers(ResourceHandlerRegistry registry)WebMvcConfigurer類的另外一個配置方法,它可以爲靜態資源URL定義自定義映射,並將它們與文件系統或應用程序類路徑上的資源進行鏈接。 在上面例子中,定義了一個經過「/ internal」 URL能夠訪問的文件的映射,以便在咱們的應用程序的「classpath:/」中查找。 (對於生產環境,可能不想將整個類路徑暴露爲靜態資源)因此讓咱們來看看咱們作了什麼,以下所示:

  • registry.addResourceHandler("/internal/**")方法,向ResourceHandlerRegistry類添加一個資源處理程序來處理咱們的靜態資源,並返回ResourceHandlerRegistration,這能夠用於以鏈式方式進一步配置映射。「/internal/**」是一個路徑模式,用於使用PathMatcher與請求URL進行匹配。 咱們已經看到在上一個示例中如何配置PathMatcher,可是默認狀況下使用AntPathMatcher實現。 能夠配置多個URL模式以匹配特定的資源位置。

  • addResourceLocations("classpath:/")方法在新建立ResourceHandlerRegistration類實例時被調用,它定義了應該從中加載資源的目錄。 這些應該是有效的文件系統或類路徑目錄,而且能夠有多個輸入。 若是提供了多個位置,將按照輸入的順序進行檢查。

咱們還可使用setCachePeriod(Integer cachePeriod)方法爲給定資源配置緩存間隔。

三. 經過EmbeddedServletContainerCustomizer調優Tomcat

Spring Boot公開了許多服務器屬性,能夠經過簡單地設置application.properties中的值來配置諸如PORT,SSL和其餘內容的服務器屬性。 可是,若是須要進行更復雜的調優,Spring Boot提供了一個EmbeddedServletContainerCustomizer接口,以編程方式定義配置。

即便會話超時能夠經過將application.properties中的server.session-timeout屬性設置爲咱們所需的值(幾秒鐘)來輕鬆配置,可是咱們仍然使用EmbeddedServletContainerCustomizer來演示該功能。

咱們但願session保持一分鐘。 爲了實現這一點,在WebConfiguration類中添加一個EmbeddedServletContainerCustomizer,其中包含如下內容:

@Bean
public 
  EmbeddedServletContainerCustomizer
  embeddedServletContainerCustomizer() {
  return new EmbeddedServletContainerCustomizer() {
    @Override
    public void 
      customize(ConfigurableEmbeddedServletContainer 
        container) {
        container.setSessionTimeout(1, TimeUnit.MINUTES);
    }
  };
}

出於演示的目的,經過調用getSession()方法來請求會話的請求對象,這將強制其建立。 爲此,咱們將添加一個新的請求映射到BookController類,代碼以下:

@RequestMapping(value = "/session", method = RequestMethod.GET)
public String getSessionId(HttpServletRequest request) {
  return request.getSession().getId();
}

啓動 ./gradlew clean bootRun

在瀏覽器中輸入 http://localhost:8080/books/session,看到以下結果:

session id

若是咱們等待一分鐘以上,而後從新加載此頁面,則session id將更改成新的 session id。

EmbeddedServletContainerCustomizer接口定義了自定義customize(ConfigurableEmbeddedServletContainer container)方法。 這實際上對於使用Java 8的人來講是一個很好的方便,由於返回一個lambda表達式,而不是建立該類的實現。 在這種狀況下,它將以下所示:

public EmbeddedServletContainerCustomizer
embeddedServletContainerCustomizer() {
      return (ConfigurableEmbeddedServletContainer container) -> {
        container.setSessionTimeout(1, TimeUnit.MINUTES);
      };
}

在應用程序啓動期間,Spring Boot自動配置檢測定製器的存在,並調用customize(...)方法,將引用傳遞給servlet容器。 在具體的狀況下,實際上獲得了一個TomcatEmbeddedServletContainerFactory實現的實例; 可是根據使用的servlet容器的種類不一樣,如Jetty或Undertow,實現方式將有所不一樣。

四. 選擇嵌入式servlet容器

儘管Tomcat是Spring Boot中的默認嵌入式容器,但並不限於此。 Spring Boot提供Jetty和Undertow的等容器的支持,所以咱們能夠選擇不一樣的容器。

若是決定使用Jetty做爲servlet容器,須要在的構建文件中添加Jetty相關的模塊。

首先,因爲Tomcat已經做爲Spring Boot的傳遞性依賴,因此咱們須要經過將如下內容添加到build.gradle中,將其從構建依賴關係樹中排除:

configurations {
  compile.exclude module: "spring-boot-starter-tomcat"
}

咱們還須要添加Jetty的編譯依賴關係:

compile("org.springframework.boot:spring-boot-starter-jetty")

由於WebConfiguration類中的RemoteIpFilterl類,是 Tomcat提供的類,因此,咱們須要把這塊代碼註釋掉。

/*
    @Bean
    public RemoteIpFilter remoteIpFilter() {
        return new RemoteIpFilter();
    }
     */

運行./gradlew clean bootRun。這時查看控制檯,出現以下信息,說明Jetty 已經運行了。

Jetty 已經啓動

這就是Spring Boot的自動配置的強大。 咱們必須從構建文件中刪除Tomcat依賴關係,以防止Tomcat和Jetty之間的依賴衝突。 Spring Boot對類路徑中的類進行條件掃描,並根據其檢測到的內容,肯定將使用哪一個servlet容器。

若是咱們查看EmbeddedServletContainerAutoConfiguration類的源代碼,會看到如下條件判讀,用於檢查Jetty包中是否存在Servlet.classServer.classLoader.class中,以肯定是否應使用JettyEmbeddedServletContainerFactory

/**
 * Nested configuration if Jetty is being used.
 */
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class})
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {

  @Bean
  public JettyEmbeddedServletContainerFactory 
      jettyEmbeddedServletContainerFactory() {
        return new JettyEmbeddedServletContainerFactory();
  }
}

@ConditionalOnClass註解告訴Spring Boot ,當 Jetty 的org.eclipse.jetty.server.Serverorg.eclipse.jetty.util.Loader類存在 classpath 中,則使用EmbeddedJetty配置。

五. 添加自定義鏈接

企業應用程序開發和部署中的另外一個常見的狀況是使用兩個單獨的HTTP端口鏈接器運行應用程序:一個用於HTTP,另外一個用於HTTPS。

前面例子中,咱們使用了 Jetty 容器,下面的例子,仍是使用默認的 tomcat,因此,須要註釋掉之前的全部 Jetty 的配置。

爲了建立HTTPS鏈接,咱們須要一些東西; 但最重要的是,須要生成用於加密和解密與瀏覽器的SSL通訊的證書密鑰庫。

若是你使用的是Unix或Mac,能夠運行如下命令:

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA

若是是 Windows 系統,使用下面命令:

"%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

在建立密鑰庫期間,你應該輸入適合您的信息,包括密碼,名稱等。 咱們將使用默認密碼:changeit。 執行完成後,新生成的密鑰庫文件將在系統主目錄(.keystore)出現。

Tips
能夠在如下位置找到有關證書密鑰庫信息:
https://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html#Prepare_the_Certificate_Keystore

密鑰庫建立完成後,須要建立一個單獨的屬性文件,以便存儲HTTPS鏈接的配置,例如端口等。 以後,建立一個配置屬性綁定對象,並使用它來配置新的鏈接。 執行如下步驟:

首先,在src/main/resources目錄下建立tomcat.https.properties文件,下面是具體的內容:

custom.tomcat.https.port=8443
custom.tomcat.https.secure=true
custom.tomcat.https.scheme=https
custom.tomcat.https.ssl=true
custom.tomcat.https.keystore=${user.home}/.keystore
custom.tomcat.https.keystore-password=changeit

接下來,在WebConfiguration類中,建立一個靜態內部類TomcatSslConnectorProperties,代碼以下:

@ConfigurationProperties(prefix = "custom.tomcat.https")
public static class TomcatSslConnectorProperties {
  private Integer port;
  private Boolean ssl= true;
  private Boolean secure = true;
  private String scheme = "https";
  private File keystore;
  private String keystorePassword;
  
// 省略了getter 和 setter 方法

  public void configureConnector(Connector connector) {
    if (port != null)
      connector.setPort(port);
    if (secure != null)
      connector.setSecure(secure);
    if (scheme != null)
      connector.setScheme(scheme);
    if (ssl!= null)
      connector.setProperty("SSLEnabled", ssl.toString());
    if (keystore!= null && keystore.exists()) {
      connector.setProperty("keystoreFile", keystore.getAbsolutePath());
      connector.setProperty("keystorePassword", keystorePassword);
    }
  }
}

如今,須要將新建立的tomcat.http.properties文件添加爲Spring Boot屬性源,並啓用TomcatSslConnectorProperties綁定。 這能夠經過在WebConfiguration類的類聲明之上添加如下代碼來完成:

@Configuration
@PropertySource("classpath:/tomcat.https.properties")
@EnableConfigurationProperties(WebConfiguration.TomcatSslConnectorProperties.class)
public class WebConfiguration extends WebMvcConfigurerAdapter {...}

最後,須要建立一個EmbeddedServletContainerFactory類的bean,用於添加HTTPS鏈接。 經過將如下代碼添加到WebConfiguration類來實現:

@Bean
public EmbeddedServletContainerFactory servletContainer(TomcatSslConnectorProperties properties) {
      TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
      tomcat.addAdditionalTomcatConnectors(createSslConnector(properties)
      );
      return tomcat;
}

private Connector createSslConnector(TomcatSslConnectorProperties properties) {
    Connector connector = new Connector();
    properties.configureConnector(connector);
    return connector;
}

啓動./gradlew clean bootRun

在瀏覽器中輸入:https://localhost:8443/internal/tomcat.https.properties,

https not trusted

點擊箭頭所指部分,而後就會下載 tomcat.https.properties 文件。

上面的程序有一些改動,除了生成密鑰庫,還建立了tomcat.https.properties配置文件,和建立了TomcatSslConnectorProperties用於屬性綁定。之前,在配置DataSource時,咱們已經處理了對application.properties中各類設置的更改。 那時候,咱們並不須要建立任何綁定對象,由於Spring Boot已經定義了它們。

如前所述,Spring Boot已經公開了許多屬性來配置應用程序設置,包括服務器配置的一整套設置。 這些值綁定到內部Spring Boot類:ServerProperties。

Tips
常見應用程序屬性的完整列表能夠在Spring Boot參考文檔中找到:http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

咱們只是簡單模仿了 Spring Boot 建立了一個配置文件,並綁定了文件中的屬性。之因此沒有使用已經存在的「server」前綴,而是選擇了「custom.tomcat」的緣由是因爲ServerProperties在檢測到未知配置字段時禁止重用命名空間並在屬性綁按期間拋出異常,由於它將一直在咱們這個例子。

@ConfigurationProperties(prefix = "custom.tomcat.https")對於TomcatSslConnectorProperties對象來講是一個很是重要的註解。它告訴 Spring Boot 自動綁定「custom.tomcat.https」前綴的屬性與TomcatSslConnectorProperties類中聲明的屬性。爲了進行綁定,除了定義類中的字段以外,定義getter和setter也是很是重要的。 還值得一提的是,在綁定過程當中,Spring將自動嘗試將屬性值轉換爲適當的數據類型。 例如,custom.tomcat.https.keystore的值自動綁定到一個專用的文件密鑰庫字段對象。

Tips
咱們以前瞭解的轉換器,也能夠轉換自定義數據類型。

下一步是告訴Spring Boot在屬性列表中包含在tomcat.https.properties中定義的屬性。 這經過在WebConfiguration類中@PropertySource("classpath:/tomcat.https.properties")註解來實現。

導入屬性值以後,須要告訴Spring Boot自動建立一個TomcatSslConnectorProperties的實例供咱們使用。 這是經過添加如下注釋來完成的:

@EnableConfigurationProperties(WebConfiguration.TomcatSslConnectorProperties.class)

完成全部屬性的設置和完成後,咱們將繼續執行代碼建立第二個鏈接。 EmbeddedServletContainerFactory bean的建立爲Spring Boot提供了一個工廠類來建立EmbeddedServletContainer。 添加靜態內部類TomcatSslConnectorProperties中configureConnector(Connector connector)方法提供了一個很好的地方來封裝和整合配置新建立的Connector實例所需的全部設置。

相關文章
相關標籤/搜索