1. 程式人生 > 程式設計 >SpringBoot自定義FailureAnalyzer過程解析

SpringBoot自定義FailureAnalyzer過程解析

這篇文章主要介紹了SpringBoot自定義FailureAnalyzer,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

官網說明

1.1 建立自己的 FailureAnalyzer

FailureAnalyzer是一種在啟動時攔截 exception 並將其轉換為 human-readable 訊息的好方法,包含在故障分析中。 Spring Boot 為 application context 相關的 exceptions,JSR-303 驗證等提供了這樣的分析器。實際上很容易建立自己的。

AbstractFailureAnalyzer是FailureAnalyzer的方便擴充套件,它檢查 exception 中是否存在指定的 exception 型別來處理。你可以從中擴充套件,這樣你的 implementation 只有在它實際存在時才有機會處理 exception。如果由於某種原因你無法處理 exception,return null給另一個 implementation 一個處理 exception 的機會。

FailureAnalyzer __mplement 將在META-INF/spring.factories中註冊:以下暫存器ProjectConstraintViolationFailureAnalyzer:

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer

1.2 排除故障 auto-configuration

Spring Boot auto-configuration 盡力'做正確的事',但有時事情會失敗,而且很難說出原因。

在 Spring Boot ApplicationContext中有一個非常有用的ConditionEvaluationReport可用。如果啟用DEBUG logging 輸出,您將看到它。如果使用spring-boot-actuator,還有一個端點,用 JSON 呈現報表。使用它來除錯 application 並檢視 Spring Boot 在執行時添加了哪些 features(以及哪些沒有)。

通過檢視 source code 和 Javadoc 可以回答更多問題。一些經驗法則:

  • 查詢名為*AutoConfiguration的 classes 並讀取它們的源,特別是@Conditional* 註釋,以找出它們啟用的 features 和何時啟用。將--debug新增到命令 line 或 System property -Ddebug以在 console 上新增 log 在您的應用程式中做出的所有 auto-configuration 決策。在 running Actuator 應用程式中,檢視autoconfig端點('/autoconfig'或 JMX 等效項)以獲取相同的資訊。
  • 查詢@ConfigurationProperties(e.g. ServerProperties)的 classes 並從那裡讀取可用的外部 configuration 選項。 @ConfigurationProperties有一個name屬性,作為外部 properties 的字首,因此ServerProperties有prefix="server",其 configuration properties 是server.port,server.address等。在 running Actuator 應用程式中檢視configprops端點。
  • 尋找使用RelaxedPropertyResolver從Environment中明確地提取 configuration 值。它通常與字首一起使用。
  • 查詢直接繫結到Environment的@Value 註釋。這不如RelaxedPropertyResolver方法靈活,但允許一些輕鬆的 binding,特別是 OS 環境變數(因此CAPITALS_AND_UNDERSCORES是period.separated的同義詞)。
  • 查詢@ConditionalOnExpression 註釋,以響應 SpEL 表示式開啟和關閉 features,通常使用從Environment解析的佔位符進行評估。

1.3 在啟動之前自定義 Environment 或 ApplicationContext

SpringApplication具有ApplicationListeners和ApplicationContextInitializers,用於將自定義應用於 context 或環境。 Spring Boot 載入了許多此類自定義項,以便在META-INF/spring.factories內部使用。註冊其他方法的方法不止一種:

  • 通過在_執行之前呼叫SpringApplication上的addListeners和addInitializers方法,以程式設計方式為每個 application。
  • 通過設定context.initializer.classes或context.listener.classes來宣告每個 application。
  • 通過新增META-INF/spring.factories並打包_appar 全部用作 library 的 jar 檔案來宣告所有 applications。

SpringApplication向 listeners 傳送一些特殊的ApplicationEvents(甚至在建立 context 之前的一些),然後為ApplicationContext釋出的 events 註冊 listeners

在使用EnvironmentPostProcessor重新整理 application context 之前,還可以自定義Environment。每個 implementation 都應該在META-INF/spring.factories中註冊:

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

implementation 可以載入任意 files 並將它們新增到Environment。例如,此 example 從 classpath 載入 YAML configuration 檔案:

public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor {

  private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

  @Override
  public void postProcessEnvironment(ConfigurableEnvironment environment,SpringApplication application) {
    Resource path = new ClassPathResource("com/example/myapp/config.yml");
    PropertySource<?> propertySource = loadYaml(path);
    environment.getPropertySources().addLast(propertySource);
  }

  private PropertySource<?> loadYaml(Resource path) {
    if (!path.exists()) {
      throw new IllegalArgumentException("Resource " + path + " does not exist");
    }
    try {
      return this.loader.load("custom-resource",path,null);
    }
    catch (IOException ex) {
      throw new IllegalStateException(
          "Failed to load yaml configuration from " + path,ex);
    }
  }

}

Environment已經準備好了 Spring Boot 預設載入的所有常用 property 源。因此,可以從環境中獲取檔案的位置。此 example 在列表末尾新增custom-resource property 源,以便在任何其他常用位置中定義的 key 優先。自定義 implementation 顯然可以定義另一個 order。

雖然在@SpringBootApplication上使用@PropertySource似乎方便且容易在Environment中載入自定義資源,但我們不推薦它為 Spring Boot 在ApplicationContext重新整理之前準備Environment。通過@PropertySource定義的任何 key 都將被載入太晚而不會對 auto-configuration 產生任何影響。

程式碼示例

2.1 指定異常分析

SpringBoot內部提供的啟動異常分析都是指定具體的異常型別實現的,最常見的一個錯誤就是埠號被佔用(PortInUseException),雖然SpringBoot內部提供一個這個異常的啟動分析,我們也是可以進行替換這一異常分析的,我們只需要建立PortInUseException異常的AbstractFailureAnalyzer,並且實現類註冊給SpringBoot即可,實現自定義如下所示

/**
 * @author WGR
 * @create 2019/11/24 -- 23:00
 */
public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
  /**
   * logger instance
   */
  static Logger logger = LoggerFactory.getLogger(PortInUseFailureAnalyzer.class);

  @Override
  protected FailureAnalysis analyze(Throwable rootFailure,PortInUseException cause) {
    logger.error("埠被佔用。",cause);
    return new FailureAnalysis("埠號:" + cause.getPort() + "被佔用","PortInUseException",rootFailure);
  }
}

註冊啟動異常分析

在上面我們只是編寫了指定異常啟動分析,我們接下來需要讓它生效,這個生效方式比較特殊,類似於自定義SpringBoot Starter AutoConfiguration的形式,我們需要在META-INF/spring.factories檔案內進行定義,如下所示:

org.springframework.boot.diagnostics.FailureAnalyzer=\
 com.topcheer.activiti.analyzer.PortInUseFailureAnalyzer

那我們為什麼需要使用這種方式定義呢?

專案啟動遇到的異常順序不能確定,很可能在Spring IOC並未執行初始化之前就出現了異常,我們不能通過@Component註解的形式使其生效,所以SpringBoot提供了通過spring.factories配置檔案的方式定義。

測試:啟動2個8080埠

啟動異常分析繼承關係

自定義的執行異常一般都是繼承自RuntimeException,如果我們定義一個RuntimeException的異常啟動分析例項會是什麼效果呢?

public class ProjectBootUnifiedFailureAnalyzer extends AbstractFailureAnalyzer<RuntimeException> {
  /**
   * logger instance
   */
  static Logger logger = LoggerFactory.getLogger(ProjectBootUnifiedFailureAnalyzer.class);

  @Override
  protected FailureAnalysis analyze(Throwable rootFailure,RuntimeException cause) {
    logger.error("遇到執行時異常",cause);
    return new FailureAnalysis(cause.getMessage(),"error",rootFailure);
  }
}

將該類也一併註冊到spring.factories檔案內,如下所示:

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.topcheer.activiti.analyze.PortInUseFailureAnalyzer,\
com.topcheer.activiti.analyze.ProjectBootUnifiedFailureAnalyzer

執行專案並測試埠號被佔用異常我們會發現,並沒有執行ProjectBootUnifiedFailureAnalyzer內的analyze方法,而是繼續執行了PortInUseFailureAnalyzer類內的方法。

那我們將PortInUseFailureAnalyzer這個啟動分析從spring.factories檔案內暫時刪除掉,再來執行專案我們會發現這時卻是會執行ProjectBootUnifiedFailureAnalyzer類內分析方法。

總結

根據本章我們瞭解了SpringBoot提供的啟動異常分析介面以及基本抽象實現類的運作原理,而且啟動異常分析存在分析泛型異常類的上下級繼承關係,異常子類的啟動分析會覆蓋掉異常父類的啟動分析,如果你想包含全部異常的啟動分析可以嘗試使用Exception作為AbstractFailureAnalyzer的泛型引數。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。