最終更新日:2020/03/23 原本2020-01-22

Spring Bootの自動設定の仕組みを理解する

Spring Bootで作るマイクロサービス 第8回

 前回までは、Spring Bootの使い方を中心に説明してきましたが、最終回である今回は、Spring Bootの内部の仕組みを紹介します。Spring Bootを使っていると、便利ではあるものの、必要な設定がどこでどのように行われているか疑問に思うことがあります。既存の設定をカスタマイズして変えたい場合には特にそのように思うことでしょう。各種ライブラリの設定を自動で行う設定機能はSpring Bootにとって大きな特徴であり、Spring Boot自体を理解するのに大いに役立つはずです。

自動設定を体験する

 ここでは、Spring Bootの自動設定機能(AutoConfiguration)を体験するために、筆者がよく利用するGsonというライブラリを使います。例えば、リスト1のコードを記述してみます。

[リスト1]Gsonライブラリを利用するサンプルプログラム(src/main/java/com/coltware/springboot/main/config/SampleApplication.javaの抜粋)
@SpringBootApplication
public class SampleApplication {

    private static final Logger log = LoggerFactory.getLogger(SampleApplication.class);

    @Autowired
    Gson gson; // (1) 自動でGsonオブジェクトを設定する

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SampleApplication.class,args);
        SampleApplication app = context.getBean(SampleApplication.class);
        app.start(context);
    }

    public void start(ApplicationContext context){
        log.info("gson : {} ",gson); // (2) 作成されたオブジェクトがあるか確認する
        : (省略)
    }
}

 (1)でGsonオブジェクトを@Autowired機能を使って自動でオブジェクトを設定します。そして(2)で作成されたオブジェクトがあるか確認します。

 実際に実行した結果がリスト2です。

[リスト2]サンプルコードの実行結果
2019-11-20 17:27:16.313  INFO 63974 --- [           main] c.c.springboot.main.SampleApplication    : Starting SampleApplication on masak.local with PID 63974 (/Users/musvia/vmshare/codezine/springboot-starter-samle/build/classes/java/main started by musvia in /Users/musvia/vmshare/codezine/springboot-starter-samle)
2019-11-20 17:27:16.315  INFO 63974 --- [           main] c.c.springboot.main.SampleApplication    : No active profile set, falling back to default profiles: default
2019-11-20 17:27:16.664  INFO 63974 --- [           main] c.c.springboot.main.SampleApplication    : Started SampleApplication in 0.652 seconds (JVM running for 0.987)
2019-11-20 17:27:16.665  INFO 63974 --- [           main] c.c.springboot.main.SampleApplication    : gson : class com.google.gson.Gson

 しかし、サンプルコードではGsonオブジェクトをどのように作成するかわかるコードは1行も記述していません。

 そこで、以下の疑問が生じるはずです。

  • Gsonオブジェクトの作成方法はどこで指定されているのか
  • Gsonオブジェクトの作成方法をカスタマイズしたい場合にはどうすればよいのか
  • 設定ファイルでどこまで指定ができるのか
  • Gsonを使わないプログラムの場合にはどうなるのか
  • どのライブラリがSpring Bootで対応しているのか
  • 独自の自動設定クラスを作るにはどうすればよいのか

 これらの疑問はSpring Bootの自動設定(Autoconfigure)を理解すれば解消できます。

自動設定機能(Autoconfigure)の概要

 Spring Bootのアプリケーションを起動する場合には、SpringApplicationクラスを通じて実行しています。

 このクラス内ではさまざまな処理を行っていますが、自動設定に関連する処理に着目し関連するクラスの関係を示したものが図1です。

図1:自動設定(Autoconfigure)の概要
図1:自動設定(Autoconfigure)の概要
  • (1)SpringApplicationは@EnableAutoConfiguraionアノテーションで自動設定を有効にします。
  • (2)@EnableAutoConfigurationの@Importアノテーションで、自動設定を行うクラスであるAutoConfigurationImportSelectorクラスを指定しています。
  • (3)AutoConfigurationImportSelectorクラス内で、SpringFactoriesLoaderクラスを利用してMETA-INF/spring.factoriesファイルを参照します。
  • (4)META-INF/spring.factoriesに記述してあるorg.springframework.boot.autoconfigure.EnableAutoConfigurationの値に各種ライブラリの設定クラスを読みます。

 また、実際のspring.factoriesのファイルの内容がリスト3です。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
// (省略)
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
// (省略)

 このファイルを見るとさまざまなライブラリ向けの設定ファイルクラスを指定していることがわかります。つまり、これらのクラスが初期状態でSpring Bootが対応しているライブラリやフレームワークということになります。

 そして、その記述の中にGsonAutoConfigurationというクラスの指定があり、そのコードを参照するとGsonオブジェクトを作成していることがわかります。

 また、これらのクラスでどのような@Beanが作成されているかはSpring Reference Documentなどを参照することでもわかります。

自動設定クラスの構造

 Gsonの自動設定を行っているGsonAutoConfigurationクラスのコードを抜粋したのがリスト4です。

@Configuration(proxyBeanMethods = false) // (1) 設定ファイルの指定
@ConditionalOnClass(Gson.class) // (2) 設定条件の指定
@EnableConfigurationProperties(GsonProperties.class) // (3) 設定ファイルとの連携
public class GsonAutoConfiguration {
    // 省略
    @Bean
    @ConditionalOnMissingBean // (4) Bean登録がなければ作成する
    public Gson gson(GsonBuilder gsonBuilder) {
        return gsonBuilder.create();
    }
    // 省略
}

 (1)が設定ファイルを示す指定です。proxyBeanMethodsを指定していますが、この指定はCGLIBというJavaコード生成ライブラリを使ったオブジェクトのサブクラスを作らない設定です。

 (2)では、@ConditionalOnClassというアノテーションがあります。このアノテーションは指定したクラスが存在するときだけ有効になるといった意味になります。従って、この場合、Gson.classというクラス定義がどこにもなければ、この設定クラスは動作しません。

 そして、(3)ではapplication.properties等の設定ファイルで指定可能な項目を定義するためのクラスを指定しています。

 (4)の@ConditionalOnMissingBeanは、どこにも作成するオブジェクトがBean登録されていない場合に、作成するという指定です。従って、自分で設定クラスを作成し、そこで該当のオブジェクトを作成している場合には、このコードは実行されません。

 多くの場合には、クラスでのアノテーションで@ConditionalOnClassを指定し、@Bean登録では@ConditionalOnMissingBeanを指定するのが通常の流れになります。こういった構造で自動設定クラスはできているので、このクラスの意味がわかれば、他のクラスも同様に理解できるはずです。

 また、自分で自動設定クラスを作成した場合には、そのクラスを含むjarファイル内のMETA-INF/spring.factoriesにリスト4のように記述すれば登録可能です。

自動設定クラスに使われる主なアノテーション

 先ほど、紹介した@ConditionalOnClassや、@ConditionalOnMissingBean以外にも、主に表1のアノテーションが存在します。

 これらのアノテーションは自動設定クラスを自作する場合以外には必要ありませんが、おおよそのどういったことができるのかを知っていると、動作の仕組みを想像しやすくなるはずです。

表1:Spring Bootの自動設定クラスを作成するときに利用する主なアノテーション
アノテーション 説明
@ConditionalOnClass 指定するクラスが存在する場合に実行される
@ConditionalOnMissingClass 指定するクラスが存在しない場合に実行される
@ConditionalOnBean 指定するオブジェクトがBean登録されている場合に実行される
@ConditionalOnMissingBean 指定するオブジェクトがBean登録されていない場合に実行される
@ConditionalOnProperty 指定したプロパティがルールに一致する場合に実行される
@ConditionalOnResource 指定したリソースがルールに一致する場合に実行される

 これらの詳しい使い方やその他のアノテーションは、Spring Boot Referenceに記述がありますので、詳しく知りたい方は参照してください。

 このようなアノテーションを使い、それぞれのライブラリやフレームワーク等の設定を組み合わせに応じて設定していくことが可能になっています。

自動設定クラスの利用可否を個別にカスタマイズする

 Spring Bootの既存で設定されている自動設定クラスを部分的に除外したい、もしくは、指定したものだけ使いたいといった場合があります。

 そういったケースはあまり多くはないかもしれませんが、どうしても自動で作成されるオブジェクトはなくしたい場合や、暗黙の動作をなるべくなくしたい場合になどに有効です。

Spring Bootの設定ファイル(@SpringBootConfiguration)

 通常のSpring Bootのアプリケーションでは、@SpringBootApplicationアノテーションが指定しています。

 このアノテーションには、リスト5のように@SpringBootConfigurationと@EnableAutoConfigurationが指定されています。

[リスト5]@SpringBootConfigurationアノテーション内のコード
@SpringBootConfiguration // (1) @Configuration同等
@EnableAutoConfiguration // (2) 自動設定クラスを使う

 (2)の指定を行ったままでは、すべての自動設定クラスが利用されてしまうので、@SpringBootApplicationが使えません。

 また、(1)は@Configurtionと同様ですが、慣例的にSpring Bootのメインアプリケーションには@SpringBootConfigurationを使います。

自動設定クラスから指定したクラスのみ除外する場合

 自動設定クラスから一部のクラスのみ除外したい場合にはリスト6のように@EnableAutoConfigurationのexcludeもしくはexcludeNameを使用します。

[リスト6]指定した自動設定クラスのみを除外する場合のSpringApplicationの記述例
@SpringBootConfiguration
@EnableAutoConfiguration(exclude = { GsonAutoConfiguration.class })
// 以下のようにも記述可能
@EnableAutoConfiguration(excludeName = { "org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration" } )

設定済みの自動設定クラスを使わないで、個別に設定クラスを指定する場合

 既存で設定されている自動設定クラスを使わないで、個別に設定クラスを使う場合には、リスト7のように@ImportAutoConfigurationを利用します。

[リスト7]指定した自動設定クラスのみを有効にする場合のSpringApplicationの記述例
@SpringBootConfiguration
@ImportAutoConfiguration({GsonAutoConfiguration.class})

自動設定や登録されているBeanを確認する

 自分で設定を作成し、カスタマイズしたものが実際どのように機能しているかを確認する場合には、起動時にDEBUG=1としてリスト8のように起動すると情報を取得することができます。

[リスト8]DEBUGオプションをつけて自動設定の状態を表示し、実行する例
$DEBUG=1 ./gradlew bootRun

============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
 : (省略)
GsonAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.google.gson.Gson' (OnClassCondition)
 :  (省略)
Negative matches:
-----------------
:   (省略)
   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
:   (省略)

 Positive matchesには、適用されたクラスとその条件が表示され、Negative matchesには適用されなかったクラスとその理由となる条件が表示されます。

 また、実際にBean登録されているオブジェクトを参照するには、リスト9のコードで参照可能です。

[リスト9]Bean登録されているオブジェクト名の一覧を取得する(src/main/java/com/coltware/springboot/main/config/SampleApplication.javaの抜粋)
: (省略)
public void start(ApplicationContext context){
    : (省略)
    String beans[] = context.getBeanDefinitionNames();
    for(int i = 0; i < beans.length; i++){
        log.info("bean name : {}",beans[i]);
    }
}
// 以下実行結果
2019-11-21 15:46:19.851  INFO 71764 --- [           main] c.c.springboot.main.SampleApplication    : bean name : org.springframework.context.annotation.internalConfigurationAnnotationProcessor
2019-11-21 15:46:19.851  INFO 71764 --- [           main] c.c.springboot.main.SampleApplication    : bean name : org.springframework.context.annotation.internalAutowiredAnnotationProcessor
2019-11-21 15:46:19.851  INFO 71764 --- [           main] c.c.springboot.main.SampleApplication    : bean name : org.springframework.context.annotation.internalCommonAnnotationProcessor
2019-11-21 15:46:19.851  INFO 71764 --- [           main] c.c.springboot.main.SampleApplication    : bean name : org.springframework.context.event.internalEventListenerProcessor
2019-11-21 15:46:19.851  INFO 71764 --- [           main] c.c.springboot.main.SampleApplication    : bean name : org.springframework.context.event.internalEventListenerFactory
2019-11-21 15:46:19.851  INFO 71764 --- [           main] c.c.springboot.main.SampleApplication    : bean name : sampleApplication

最後に

 Spring Bootの最も大きな特徴であると同時に暗黙的に使用していることが多いため、最もわかりにくい部分の1つが自動設定(Autoconfigure)です。

 今回の自動設定の仕組みを知る過程で、Spring BootがWebアプリケーションの範囲だけではなく、さまざまなライブラリを考慮して作られていることがわかるはずです。Spring Bootに慣れてくるとさまざまなカスタマイズをしたり、Spring Bootを使ってアプリケーションを作ったりするケースも出てくると思います。

 筆者もこの仕組みを知ったことで、実装側における多少の動作の違いを設定ファイル等で分けるのではなく、自動設定用ライブラリを作成し、それを切り替えることで動作を分けるようにしています。例えば、テスト向けの外部モックが含まれるような場合の設定と、本番用の設定といった形に分けます。

 今回紹介したSpring Bootの自動設定の仕組みを理解していれば、Spring Bootを使う上でわからないことがあっても、自分で調べることができるのではないかと思います。

 また、Spring Bootには、本連載で紹介しきれなかった機能もありますが、これまでの内容を参考にSpring Bootの新たな側面を発見していただければ幸いです。

参考資料