経緯
こちらを参照
https://qiita.com/ShassBeleth/items/476436b7c81fe81ddbb2
まぁ指定のポートがすでに使われてた場合のExceptionは
もともとSpring Bootに用意されてるんだけどね
上書きもできるし追加もできるよっていう話
環境
Spring Boot 2.2.2.RELEASE - 2.2.4.RELEASE
Kotlin 1.3.61
Gradle
環境は以下の記事のまんま
Spring Boot側が用意してくれてるFailureAnalyzer
例外つかむにはAbstractFailureAnalyzer
を継承する必要がある
検索かけて出てきたクラスがこれ
全部で17個
さらにそれぞれのクラスを継承するかもしれないからもっとあるかも
ポートが既に使われてた時に呼ばれるExceptionはPortInUseException
それをつかむのがPortInUseFailureAnalyzer
class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
return new FailureAnalysis("Web server failed to start. Port " + cause.getPort() + " was already in use.",
"Identify and stop the process that's listening on port " + cause.getPort() + " or configure this "
+ "application to listen on another port.",
cause);
}
}
中のクラスはこんな感じ
Boot Runでアプリを起動すると・・・
こんな感じ
FailureAnalyzerにあるDescriptionとActionがログに書き込まれてるっていうね
Spring Bootが用意してるFailureAnalyzerを独自のものに書き換える
正直おすすめはできない
class MyPortInUseExceptionFailureAnalyzer : AbstractFailureAnalyzer<PortInUseException>() {
override fun analyze(rootFailure: Throwable, cause: PortInUseException): FailureAnalysis {
return FailureAnalysis(
cause.message,
"Port already in use!!!",
cause
)
}
}
AbstractFailureAnalyzer<PortInUseException>
を継承したクラスを作っておく
resources/META-INF/
フォルダ配下にspring.factories
ファイルを作って
org.springframework.boot.diagnostics.FailureAnalyzer =\
com.example.spring.boot.web.failure.analyzer.MyPortInUseFailureAnalyzer
その中身はこんな感じ
これで登録OK
また適当にポート8080を裏で使っておいてからBoot Runすると
上書きできましたー
おすすめしないっていうのも例外時に出力される情報を変更できちゃうので、実際に例外が発生したときに何があったかわからなくなるから
出力される内容は変えずに処理をはさんだりとかするのがいいのかな・・・?
FailureAnalyzerのカスタマイズの使い道としては以下の方が実用的かも
独自のExceptionをFailureAnalyzerでつかむ
/**
* 独自の例外
*/
class SampleException(message: String?) : RuntimeException(message)
/**
* カスタムFailureAnalyzerのサンプル
* アプリ起動中に発生したSampleExceptionをつかむ
*/
class SampleFailureAnalyzer : AbstractFailureAnalyzer<SampleException>() {
override fun analyze(rootFailure: Throwable, cause: SampleException): FailureAnalysis {
return FailureAnalysis(
cause.message,
"Sample Exception!",
cause
)
}
}
こんな感じに独自の例外を作っておいて、それをつかむFailureAnalyzerを作成しておく
spring.factories
はこんな感じ
org.springframework.boot.diagnostics.FailureAnalyzer =\
com.example.spring.boot.web.failure.analyzer.MyPortInUseFailureAnalyzer ,\
com.example.spring.boot.web.failure.analyzer.SampleFailureAnalyzer
複数のFailureAnalyzerを登録することもできます
で、作ったSampleFailureAnalyzerがちゃんとつかめるか確認したいから
以下を適当に作っておく
/**
* アプリ起動中に読み込まれるサンプルComponent
*/
@Component
class SampleComponent {
fun run() {
throw SampleException("exception!")
}
}
/**
* リフレッシュ時にイベント発火するListenerクラス
*/
@Component
class SampleApplicationListener : ApplicationListener<ContextRefreshedEvent?> {
@Autowired
private val sampleComponent: SampleComponent? = null
/**
* リフレッシュ時にSampleComponentのrunを実行する(例外を発生させる)
*/
override fun onApplicationEvent(event: ContextRefreshedEvent) {
sampleComponent!!.run()
}
}
- リフレッシュ時にSampleApplicationListenerのonApplicationEventが呼ばれる
- sampleComponentのrunが呼ばれる
- runの中でExceptionが発生する
って感じ
これでアプリ起動時にエラーが発生するようになりました
実行してみると
ちゃんとメッセージ出せてます!
spring.factoriesを登録していない場合はスタックトレースが表示されます
まとめ
- FailureAnalyzerでアプリ起動中の例外もつかめる!
- 既にSpring Bootが用意してくれてるものも上書きできる!
- 自分で作った例外もつかめる!
スタックトレースのほうがわかりやすい場合もあるし、一概にすべてのExceptionをつかんだほうがいいとは言えないのかな・・・?
でも把握できてるExceptionかどうか、発生しうる例外かどうかの判断はできるし、まぁまぁ便利だなとは思う