Spring Bootアプリケーションが起動するまでにやっていること

本記事は、Spring Boot 2.7.3について書いてあります。

Spring Bootとは

Spring Bootはフレームワークでなく、Spring Frameworkを利用してアプリケーションを作りやすくしている仕組みのこと。 Spring Boot単体では何も作ることはできないので、Spring WebやSpring Dataなどのフレームワークを利用する。(Spring BootではStarterを利用することがほとんど)

Spring Bootと非Spring Bootの大きな違いは、Starterという依存関係便利パックが提供されている&デフォルトの設定(必要そうなクラスのBean登録)がされていること。

Starters

アプリケーションを作るときにあったら必要であろう依存関係を含んだ便利パック。例えば、SpringとJPAを利用してアプリケーションを作りたいとなったら、spring-boot-starter-data-jpaを依存関係に追加すればよい。 Starterを利用すると後述するauto-configurationの仕組みが提供されており、単にライブラリを依存関係に追加するよりも設定項目が減り、楽にアプリケーション開発が行える。 ※ 公式のスターターはspring-boot-starter-*という名前になっており多くのIDEでそれに適した設定が入っているため、サードパーティのスターターは頭にspring-bootという名前をつけてはいけない。

Starterの例

  • Spring Boot application starters
    • Spring Bootでアプリケーションを作るために利用
    • spring-boot-starter-data-jpa、spring-boot-starter-webなど
  • Spring Boot production starters
    • production readyな機能を提供する
    • spring-boot-starter-actuator
  • Spring Boot technical starters
    • デフォルトで利用されているライブラリ等を置き換えたり除外したりするときに利用
    • spring-boot-starter-loggingã‚„spring-boot-starter-tomcatなど

コーディング

Spring Bootアプリケーションにおけるエントリーポイントは@SpringBootApplicationで、これをメインクラスに付与する。 なお、@SpringBootApplicationはパッケージ最上位に置くのがよい。

一般的なメインクラス。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

設定クラス

Spring Bootの設定にはJavaベースの設定を推奨している。XMLを利用することもできる。

Auto-configuration

Spring Boot auto-configurationは、プロジェクトに追加されている依存関係に基づいて、Spring アプリケーションに自動的に設定を行う仕組みのことである。 @EnableAutoConfigurationまたは@SpringBootApplicationを付与すると、auto-configurationを有効にすることができる。

デフォルトで全AutoConfigurationが適用されるが、以下のように書くとauto-configurationから除外することができる。

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class Application {
}

@SpringBootApplicationについて

@SpringBootApplicationは、@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScanを含み持つアノーテーションでこれらと同じ機能を提供する。ちなみに、複数の機能を持つアノテーションのことを複合アノテーションと呼ぶ。

  • @EnableAutoConfiguration
    • 先に説明したauto-configurationを有効にする
  • @ComponentScan
  • @SpringBootConfiguration
    • このクラス自身をBean登録する。
    • @Configurationとほぼ同じ機能ではあるが、インテグレーションテストを書く際に役立つ。

Auto configuration

AutoConfigurationのクラスに@AutoConfigurationを付与するとBean登録される。 auto-configurationを適用させるクラスはMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 2.7より前はMETA-INF/spring.factoriesだった)に列挙されている。

AutoConfigurationImportSelector#getCandidateConfigurationsを参照。

Condition Annotations

auto-configurationクラスに書いてある@Conditonalに従ってBean登録が行われる。 @Conditionalの仲間には次のものがある。

Class Condition

クラスパスに指定したクラスがあるかないか。@ConditionalOnClass、@ConditionalOnMissingClass。 JacksonAutoConfiguration.javaだと、ObjectMapper.javaがクラスパスに存在したらauto-configurationが適用される。

@AutoConfiguration
@ConditionalOnClass(ObjectMapper.class)
public class JacksonAutoConfiguration {

Bean Conditions

指定したBeanがあるかないか。@ConditionalOnBean、@ConditionalOnMissingBean。

RestTemplateAutoConfiguration#restTemplateBuilderConfigurerだと、RestTemplateBuilderConfigurerがBean登録されてなかったらこのメソッドでRestTemplateBuilderConfigurerのBean登録が行われる。

@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer(

Property Conditions

application.ymlや起動オプションなどで指定するSpring 環境プロパティによってBean登録が行われる。

JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration.javaだと、spring.mvc.converters.preferred-json-mapper=jacksonのときBean登録が行われる

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnBean(ObjectMapper.class)
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
        havingValue = "jackson", matchIfMissing = true)
static class MappingJackson2HttpMessageConverterConfiguration {

Resource Conditions

指定したリソースが存在したらBean登録する。@ConditionalOnResource。

このようにresourcesにパスを指定する。(ProjectInfoAutoConfiguration#buildProperties)

@ConditionalOnResource(resources = "${spring.info.build.location:classpath:META-INF/build-info.properties}")

Web Application Conditions

Webアプリケーションかどうか。@ConditonalOnWebApplication、@ConditionalOnNotWebAplication。 その他、クラウドプラットフォームかどうかで判定する@ConditionalOnCloudPlatformなどもある。CloudPlatform.javaに定義されている、CloudFoundry、Heroku、SAP、Kubernetes、Azure App Serviceが対象。

SpEL Expression Conditions

@ConditionalOnExpressionはSpEL(SPring Expression Language)で書かれたプロパティに応じてBean登録する。

Spring Boot が起動するまでにやっていること

@EnableAutoConfiguraion(@SpringBootApplication)が行っていることから、Spring Bootの特徴であるAutoConfigureの仕組みを説明する。

1. DIコンテナ(ApplicationContext)の作成

ApplicationContextは、BeanFactoryを継承したアプリケーションの設定を提供する主要なインターフェース。メイン機能の1つにDIコンテナがある。

2. spring.factories、AutoConfiguration.imports読込

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsにauto-configureするクラス(@AutoConfigureをつけたクラス)を列挙する。 以前はspring.factoriesに書いていたが、このファイルはauto-configureを利用するために必要なクラスを書くようになった(ぽい)

これらに書かれたクラスを読み込む。

3. Bean登録するAutoConfigurationクラスをソート

定義された順序で、Bean登録するAutoConfigurationクラスをソートする。AutoConfigurationSorter#getInPriorityOrderで行う。

  1. アルファベット順
  2. @Orderの順
  3. @AutoConfigureBeforeと@AutoConfigureAfterの順
    1. @AutoConfigureBeforeは指定したクラスの前
    2. @AutoConfigureAfterは指定したクラスの後

4. Bean登録

ConfigurationClassParser#processGroupImportsでDIコンテナにBean登録を行う

application.properties(yml)について

application.propterties(yml)は、設定値を書くファイル。 Spring Bootが提供している設定値の一覧はここに書いてある。

このファイルに書いても確実に設定されるわけではなく、@ConfigurationPropertiesが付与されたクラスに値がバインドされるだけである。

設定が行われる条件は、次の2つが満たされていることです。

  • バインドされる@ConfigurationPropertiesのクラスがBean登録されていること
  • そのAutoConfigurationが有効となっていこと

大抵、@AutoConfigurationを付与したクラスに@EnableConfigurationPropertiesも付与されている。つまり、AutoConfigurationが有効になっていればapplication.properties(yml)に書いた設定は適用される。

これはDataSourceAutoConfiguration.javaの例である。

@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import(DataSourcePoolMetadataProvidersConfiguration.class)
public class DataSourceAutoConfiguration {

Banner

Bannerとは、Spring Bootを起動すると見るこれのことで地味に変更がされ続けている。

歴史

バージョン 内容
1.0.x Bannerを表示するかどうかの設定のみ
1.1.x Text Bannerを表示できるようになる
1.2.x Banner変数が登場
1.3.x Text Bannerが色付け可能に
1.4.x Image Bannerを表示可能に
2.0.x アニメーションgifをサポート
2.2.x ASCII バナーは256色利用可能に

カスタマイズ

バナーのカスタマイズ方法は3通りで、デフォルトバナーは3で実現しているおり、SpringBootBanner.javaが実装。

  1. resources/banner.[ gif | jpg | png ]を置く
  2. resources/banner.txtを置く
  3. org.springframework.boot.Bannerインターフェースを実装する

優先順位

SpringApplicationBannerPrinter#getBannerで表示すバナーを決定している。

  1. Image BannerとText Bannerを探す
  2. 1がなかったら自作のBannerクラス
  3. 1も2もなかったらDefault Banner

Image Banner と Text Bannerは一緒に表示可能。

Bannerインターフェース実装してみた

public class StoresBanner implements Banner {

    private static final String[] BANNER = {
            "  ______    _________     ___     _______      ________    ______   \\n" +
            ".' ____ \\\\  |  _   _  |  .'   `.  |_   __ \\\\    |_   __  | .' ____ \\\\  \\n" +
            "| (___ \\\\_| |_/ | | \\\\_| /  .-.  \\\\   | |__) |     | |_ \\\\_| | (___ \\\\_| \\n" +
            " _.____`.      | |     | |   | |   |  __ /      |  _| _   _.____`.  \\n" +
            "| \\\\____) |    _| |_    \\\\  `-'  /  _| |  \\\\ \\\\_   _| |__/ | | \\\\____) | \\n" +
            " \\\\______.'   |_____|    `.___.'  |____| |___| |________|  \\\\______.' \\n"
    };

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
        for (String line : BANNER) {
            printStream.println(line);
        }
    }
}
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application11.class);
        app.setBanner(new StoresBanner());
        app.run(args);
    }

}

※ バナーが好きだと思われる人が作ったOnline Spring Boot Banner Generatorというサイトがある。