自動設定クラスの構造
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のアノテーションが存在します。
これらのアノテーションは自動設定クラスを自作する場合以外には必要ありませんが、おおよそのどういったことができるのかを知っていると、動作の仕組みを想像しやすくなるはずです。
アノテーション | 説明 |
---|---|
@ConditionalOnClass | 指定するクラスが存在する場合に実行される |
@ConditionalOnMissingClass | 指定するクラスが存在しない場合に実行される |
@ConditionalOnBean | 指定するオブジェクトがBean登録されている場合に実行される |
@ConditionalOnMissingBean | 指定するオブジェクトがBean登録されていない場合に実行される |
@ConditionalOnProperty | 指定したプロパティがルールに一致する場合に実行される |
@ConditionalOnResource | 指定したリソースがルールに一致する場合に実行される |
これらの詳しい使い方やその他のアノテーションは、Spring Boot Referenceに記述がありますので、詳しく知りたい方は参照してください。
このようなアノテーションを使い、それぞれのライブラリやフレームワーク等の設定を組み合わせに応じて設定していくことが可能になっています。
自動設定クラスの利用可否を個別にカスタマイズする
Spring Bootの既存で設定されている自動設定クラスを部分的に除外したい、もしくは、指定したものだけ使いたいといった場合があります。
そういったケースはあまり多くはないかもしれませんが、どうしても自動で作成されるオブジェクトはなくしたい場合や、暗黙の動作をなるべくなくしたい場合になどに有効です。
Spring Bootの設定ファイル(@SpringBootConfiguration)
通常のSpring Bootのアプリケーションでは、@SpringBootApplicationアノテーションが指定しています。
このアノテーションには、リスト5のように@SpringBootConfigurationと@EnableAutoConfigurationが指定されています。
@SpringBootConfiguration // (1) @Configuration同等 @EnableAutoConfiguration // (2) 自動設定クラスを使う
(2)の指定を行ったままでは、すべての自動設定クラスが利用されてしまうので、@SpringBootApplicationが使えません。
また、(1)は@Configurtionと同様ですが、慣例的にSpring Bootのメインアプリケーションには@SpringBootConfigurationを使います。
自動設定クラスから指定したクラスのみ除外する場合
自動設定クラスから一部のクラスのみ除外したい場合にはリスト6のように@EnableAutoConfigurationのexcludeもしくはexcludeNameを使用します。
@SpringBootConfiguration @EnableAutoConfiguration(exclude = { GsonAutoConfiguration.class }) // 以下のようにも記述可能 @EnableAutoConfiguration(excludeName = { "org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration" } )
設定済みの自動設定クラスを使わないで、個別に設定クラスを指定する場合
既存で設定されている自動設定クラスを使わないで、個別に設定クラスを使う場合には、リスト7のように@ImportAutoConfigurationを利用します。
@SpringBootConfiguration @ImportAutoConfiguration({GsonAutoConfiguration.class})
自動設定や登録されているBeanを確認する
自分で設定を作成し、カスタマイズしたものが実際どのように機能しているかを確認する場合には、起動時にDEBUG=1としてリスト8のように起動すると情報を取得することができます。
$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のコードで参照可能です。
: (省略) 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の新たな側面を発見していただければ幸いです。
参考資料
本連載の書籍が発売されました!
Javaによる高速Webアプリケーション開発のためのSpring Boot入門
著者:WINGSプロジェクト 小林昌弘
発売日:2020年5月31日(水)
価格(POD):2,200円(税込)
価格(電書):1,760円(税込)