接上文 Spring Developer Tools 源碼分析:二、類路徑監控,接下來看看前面提到的這些類是如何配置,如何啟動的。
spring-boot-devtools 使用了 Spring Boot 的自動配置方式,我們先關注本地開發環境中自動重新開機的部分。
在
LocalDevToolsAutoConfiguration
主要包含了 LiveReload 和重新開機的配置,LiveReload 後續看情況再介紹,這裡先看重新開機的配置。
3.1 LocalDevToolsAutoConfiguration
本地配置
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();
}