天天看點

Spring Developer Tools 源碼分析:三、重新開機自動配置

接上文 ​​Spring Developer Tools 源碼分析:二、類路徑監控​​,接下來看看前面提到的這些類是如何配置,如何啟動的。

spring-boot-devtools 使用了 Spring Boot 的自動配置方式,我們先關注本地開發環境中自動重新開機的部分。

在 ​

​LocalDevToolsAutoConfiguration​

​​ 主要包含了 ​​LiveReload​​ 和重新開機的配置,LiveReload 後續看情況再介紹,這裡先看重新開機的配置。

3.1 ​

​LocalDevToolsAutoConfiguration​

​ 本地配置

@Configuration
@ConditionalOnInitializedRestarter
@EnableConfigurationProperties(DevToolsProperties.class)
public class LocalDevToolsAutoConfiguration      

在這個類上 ​

​@ConditionalOnInitializedRestarter​

​​ 是一個該配置生效的條件,具體實作中會判斷 ​

​Restarter​

​​ 是否已經執行個體化,并且是否存在可被監控的類目錄(除 ​

​jar​

​ 檔案外的目錄),如果已經執行個體化,并且有需要監控的目錄才會啟動。

通過 java -jar 方式啟動的時候,由于不存在需要監控的目錄,devtools 不會觸發後續的配置,是以雖然啟動了 Restarter,但是并不會監控目錄,也不會自動重新開機。

當通過 IDE 啟動項目時,預設情況下會滿足這裡的條件,​

​LocalDevToolsAutoConfiguration​

​ 中的其他配置可以生效。

3.2 RestartConfiguration 重新開機配置

/**
 * Local Restart Configuration.
 */
@Configuration
@ConditionalOnProperty(
    prefix = "spring.devtools.restart", 
    name = "enabled", 
    matchIfMissing = true)
static      

在 ​

​RestartConfiguration​

​ 上也有限制條件,隻有當設定下面的參數時才不會生效:

spring.devtools.restart.enabled=false      

不設定或者設定為任何不是 ​

​false​

​ (忽略大小寫)的值時,都會生效。

除了這個參數能控制外,還有一個可以直接控制 Restarter 是否生效的參數,後續會介紹。

下面逐個看 ​

​RestartConfiguration​

​ 中的各個配置。

3.2.1 FileSystemWatcherFactory 檔案監控

配置代碼如下:

@Bean
public FileSystemWatcherFactory fileSystemWatcherFactory() {
    return this::newFileSystemWatcher;
}

private FileSystemWatcher newFileSystemWatcher() {
    Restart restartProperties = this.properties.getRestart();
    FileSystemWatcher watcher = new FileSystemWatcher(true,
            restartProperties.getPollInterval(),
            restartProperties.getQuietPeriod());
    String triggerFile = restartProperties.getTriggerFile();
    if (StringUtils.hasLength(triggerFile)) {
        watcher.setTriggerFilter(new TriggerFileFilter(triggerFile));
    }
    List<File> additionalPaths = restartProperties.getAdditionalPaths();
    for (File path : additionalPaths) {
        watcher.addSourceFolder(path.getAbsoluteFile());
    }
    return      

​FileSystemWatcherFactory​

​​ 是一個函數式接口,這裡直接傳回了一個方法引用。将來調用該接口的方法時,就會執行下面的 ​

​newFileSystemWatcher​

​ 方法。

每次調用 ​

​fileSystemWatcherFactory()​

​​ 方法時,傳回的都是同一個 ​

​FileSystemWatcherFactory​

​​,但是調用工廠的 ​

​getFileSystemWatcher()​

​​ 方法時傳回都是新的 ​

​FileSystemWatcher​

​。

通過這個方法可以看到建立 ​

​FileSystemWatcher​

​ 時,這裡會判斷是否配置了觸發檔案(隻有修改指定檔案才會重新開機),是否配置額外需要監控變化的位置。

3.2.2 ClassPathRestartStrategy 重新開機政策

代碼如下:

@Bean
@ConditionalOnMissingBean
public ClassPathRestartStrategy classPathRestartStrategy() {
    return new PatternClassPathRestartStrategy(
            this.properties.getRestart().getAllExclude());
}      

該方法傳回了 ​

​PatternClassPathRestartStrategy​

​ 實作類,判斷是否重新開機時,忽略所有傳入的位置,預設忽略的位置如下:

private static final String DEFAULT_RESTART_EXCLUDES = "META-INF/maven/**,"
        + "META-INF/resources/**,resources/**,static/**,public/**,templates/**,"
        + "**/*Test.class,**/*Tests.class,git.properties,META-INF/build-info.properties";      

額外的排除項可以通過下面的參數設定:

spring.devtools.restart.additional-exclude=      

3.2.3 ClassPathFileSystemWatcher 類路徑監控

代碼如下:

@Bean
@ConditionalOnMissingBean
public ClassPathFileSystemWatcher classPathFileSystemWatcher() {
    URL[] urls = Restarter.getInstance().getInitialUrls();
    ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(
            fileSystemWatcherFactory(), classPathRestartStrategy(), urls);
    watcher.setStopWatcherOnRestart(true);
    return      

這裡建立時,從 ​

​Restarter​

​​ 擷取了需要監控的類路徑(後續會詳細介紹),然後建立了一個 ​

​watcher​

​​,使用了前面建立的兩個 bean。​

​setStopWatcherOnRestart​

​​ 的意思是當類路徑發生變化并且需要重新開機時,是否停止類監控,這裡設定了 ​

​true​

​,也就是重新開機前會停止監控。

如果重新開機前會停止監控,我們可能需要擔心如果配置改錯了導緻 Spring 無法啟動該怎麼辦,devtools 也提供了相應的政策來解決這個問題,下一小節就會看到。

3.2.3 監聽 ClassPathChangedEvent

代碼如下:

@EventListener
public void onClassPathChanged(ClassPathChangedEvent event) {
    if (event.isRestartRequired()) {
        Restarter.getInstance().restart(
                new      

當一個方法添加 ​

​@EventListener​

​​ 時,Spring 會把該方法添加到事件監聽中,當觸發該事件時,這個方法就會被調用。在上一篇介紹了 ​

​ClassPathChangedEvent​

​​,其中包含了 ​

​restartRequired​

​​,這裡會判斷是否需要重新開機,當需要重新開機時,就會調用 ​

​Restarter​

​ 的執行個體進行重新開機。

在重新開機方法中還傳入了 ​

​FileWatchingFailureHandler​

​​ 并且使用了 ​

​fileSystemWatcherFactory​

​。

​FileWatchingFailureHandler​

​​ 實作了 ​

​FailureHandler​

​​ 接口,該接口用于在重新開機的啟動過程中,如果出錯了,要采取什麼政策去進行下去,該接口方法傳回的結果隻有兩種,​

​Outcome.ABORT​

​​ 中止或 ​

​Outcome.RETRY​

​ 重試。

​FileWatchingFailureHandler​

​​ 中會通過 ​

​fileSystemWatcherFactory​

​ 建立一個新的檔案監控,當類路徑的内容發生變化時(不需要考慮是否需要重新開機,因為已經停止,并且沒啟動成功)就嘗試重新啟動。這種政策可以解決當配置或者代碼出錯無法啟動時,可以修改BUG解決錯誤,然後 devtools 自動嘗試啟動。

​FileWatchingFailureHandler​

​​ 中的 ​

​FileSystemWatcher​

​ 和 3.2.3 中的不是同一個,并且在重新開機前另一個已經關閉,關閉的目的不是為了防止和這裡存在兩個監控出現沖突,而是為了防止第一次修改後,還沒有重新開機時又發生了變化,由于重新開機需要時間,這就會導緻重新開機還沒完成就又重新開機了,這種情況下除了會産生錯誤外,還會因為短時間内頻繁重新開機導緻重新開機時間過長。

在重新開機時,會先關閉所有的 Spring Context,此時也會觸發 ​

​ClassPathFileSystemWatcher​

​​ 中的 ​

​destroy​

​ 方法:

@Override
public void destroy() throws Exception {
    this.fileSystemWatcher.stop();
}      

3.3 重新開機後會重新初始化 Spring