Spring BootでのWebMvcのJavaコンフィグレーション

最近、個人的にSpring Bootを使ったWebアプリケーション開発について学んでいます。ちょっとしたWebベースのアプリケーションをJavaで開発したいと思い、Spring Bootであればサクッと作れそうと思ったからです。。が、私自身Spring BootどころかSpringの知識さえも余りないので、「Spring徹底入門」や公式ドキュメント及びソースリーディングしながら、写経とデバッグで動きを追いながら、少しずつ動きや仕組みについて理解を進めているところです。

今回は、Spring BootでWebMvcベースのアプリケーション開発を行なう時、Javaベースのコンフィグレーションってどうするんだっけ?と、そもそも仕組み周りから理解できていなかったので整理してみました。

Auto-configuration

Spring Bootには、Auto-configurationという、最小限の設定でSpringアプリケーションを容易に構築できる仕組みがあります。例えば、必要な条件が満たされる場合(例えば特定のクラスがクラスパス上に存在しているまたは存在していない、など)にBean定義が有効になるといった具合です。以下は、CassandraAutoConfigurationクラスの一例で、アノテーション@ConditionalOnClassで、クラスパスにorg.apache.catalina.Clusterクラスが存在する場合に自動設定定義が有効化されるようになっています。

出典 https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java

Spring Bootでは、著名な様々なソフトウェアの自動設定を行なうAutoConfigureクラスが提供されています。以下は、spring-bootのautoconfigureプロジェクトのリポジトリですが、cassandraやmongo、solrなどのAutoConfigurationクラスが用意されているのが分かります。

https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure

AutoConfigure機能は、spring-boot-autoconfigureというプロジェクトで定義されており、spring-boot-starterプロジェクトをライブラリとして追加すると、依存ライブラリとして追加されます。

さて、Spring BootでWebアプリケーション開発を行なうためのstarterライブラリとして、spring-boot-starter-webがあります。starter-webの依存ライブラリには、spring-webspring-webmvcがあり、Spring MVCを使ったWebアプリケーション開発のベースが整います。

以下は、Spring Bootのバージョン2.0.4.RELEASEで、spring-boot-starter-webを依存関係のcompileに指定した場合の依存関係グラフです。

compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
\--- org.springframework.boot:spring-boot-starter-web -> 2.0.4.RELEASE
     +--- org.springframework.boot:spring-boot-starter:2.0.4.RELEASE
     |    +--- org.springframework.boot:spring-boot:2.0.4.RELEASE
     |    |    +--- org.springframework:spring-core:5.0.8.RELEASE
     |    |    |    \--- org.springframework:spring-jcl:5.0.8.RELEASE
     |    |    \--- org.springframework:spring-context:5.0.8.RELEASE
     |    |         +--- org.springframework:spring-aop:5.0.8.RELEASE
     |    |         |    +--- org.springframework:spring-beans:5.0.8.RELEASE
     |    |         |    |    \--- org.springframework:spring-core:5.0.8.RELEASE (*)
     |    |         |    \--- org.springframework:spring-core:5.0.8.RELEASE (*)
     |    |         +--- org.springframework:spring-beans:5.0.8.RELEASE (*)
     |    |         +--- org.springframework:spring-core:5.0.8.RELEASE (*)
     |    |         \--- org.springframework:spring-expression:5.0.8.RELEASE
     |    |              \--- org.springframework:spring-core:5.0.8.RELEASE (*)
     |    +--- org.springframework.boot:spring-boot-autoconfigure:2.0.4.RELEASE
     |    |    \--- org.springframework.boot:spring-boot:2.0.4.RELEASE (*)
     |    +--- org.springframework.boot:spring-boot-starter-logging:2.0.4.RELEASE
     |    |    +--- ch.qos.logback:logback-classic:1.2.3
     |    |    |    +--- ch.qos.logback:logback-core:1.2.3
     |    |    |    \--- org.slf4j:slf4j-api:1.7.25
     |    |    +--- org.apache.logging.log4j:log4j-to-slf4j:2.10.0
     |    |    |    +--- org.slf4j:slf4j-api:1.7.25
     |    |    |    \--- org.apache.logging.log4j:log4j-api:2.10.0
     |    |    \--- org.slf4j:jul-to-slf4j:1.7.25
     |    |         \--- org.slf4j:slf4j-api:1.7.25
     |    +--- javax.annotation:javax.annotation-api:1.3.2
     |    +--- org.springframework:spring-core:5.0.8.RELEASE (*)
     |    \--- org.yaml:snakeyaml:1.19
     +--- org.springframework.boot:spring-boot-starter-json:2.0.4.RELEASE
     |    +--- org.springframework.boot:spring-boot-starter:2.0.4.RELEASE (*)
     |    +--- org.springframework:spring-web:5.0.8.RELEASE
     |    |    +--- org.springframework:spring-beans:5.0.8.RELEASE (*)
     |    |    \--- org.springframework:spring-core:5.0.8.RELEASE (*)
     |    +--- com.fasterxml.jackson.core:jackson-databind:2.9.6
     |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0
     |    |    \--- com.fasterxml.jackson.core:jackson-core:2.9.6
     |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.6
     |    |    +--- com.fasterxml.jackson.core:jackson-core:2.9.6
     |    |    \--- com.fasterxml.jackson.core:jackson-databind:2.9.6 (*)
     |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.6
     |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0
     |    |    +--- com.fasterxml.jackson.core:jackson-core:2.9.6
     |    |    \--- com.fasterxml.jackson.core:jackson-databind:2.9.6 (*)
     |    \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.6
     |         +--- com.fasterxml.jackson.core:jackson-core:2.9.6
     |         \--- com.fasterxml.jackson.core:jackson-databind:2.9.6 (*)
     +--- org.springframework.boot:spring-boot-starter-tomcat:2.0.4.RELEASE
     |    +--- javax.annotation:javax.annotation-api:1.3.2
     |    +--- org.apache.tomcat.embed:tomcat-embed-core:8.5.32
     |    +--- org.apache.tomcat.embed:tomcat-embed-el:8.5.32
     |    \--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.32
     |         \--- org.apache.tomcat.embed:tomcat-embed-core:8.5.32
     +--- org.hibernate.validator:hibernate-validator:6.0.11.Final
     |    +--- javax.validation:validation-api:2.0.1.Final
     |    +--- org.jboss.logging:jboss-logging:3.3.2.Final
     |    \--- com.fasterxml:classmate:1.3.4
     +--- org.springframework:spring-web:5.0.8.RELEASE (*)
     \--- org.springframework:spring-webmvc:5.0.8.RELEASE
          +--- org.springframework:spring-aop:5.0.8.RELEASE (*)
          +--- org.springframework:spring-beans:5.0.8.RELEASE (*)
          +--- org.springframework:spring-context:5.0.8.RELEASE (*)
          +--- org.springframework:spring-core:5.0.8.RELEASE (*)
          +--- org.springframework:spring-expression:5.0.8.RELEASE (*)
          \--- org.springframework:spring-web:5.0.8.RELEASE (*)

spring-webspring-webmvc、そしてspring-boot-autoconfigureも依存ライブラリとしてクラスパスに追加されます。そして、webmvcで必要となるコンフィグレーションもautoconfigureライブラリに内包されており、すぐにSpring MVCでWebアプリケーション開発が行えるようになっているようです。

Spring BootでMVCのWebアプリケーション開発をするための下地は、STS(Spring Tool Suite)を使ってプロジェクト「Spring Boot>Spring Starter Project」作成をすると、すぐに構築できます。

組み込みたい依存ライブラリを選択します。

また、同様な事は、以下のウェブページからも可能です。

https://start.spring.io

@EnableWebMvc

SpringでのWebMvcコンフィグレーション

Springでは、アノテーションorg.springframework.web.servlet.config.annotation.EnableWebMvcをクラス定義に付与する事で、Spring MVCが提供してくれているコンフィグレーションクラスがインポートされ、必要となるコンポーネントのBean定義が自動で行われるようになっています。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

EnableWebMvcの定義を見ると、アノテーション@Importで、org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfigurationクラスがインポートされるようになっています。このクラスは、基本的なコンフィグレーションを担うWebMvcConfigurationSupportクラスを継承しています。また、WebMvcConfigurerCompositeクラスをメンバ変数に持ち、WebMvcConfigurerオブジェクトらにコンフィグレーションを委譲するようになっています。WebMvcConfigurerオブジェクトは、@Autowiredでインジェクトされるようになっています。

以下、各クラスの実装の抜粋です。(Branch 5.0.x)

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }

    ...
}

参考 https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java

class WebMvcConfigurerComposite implements WebMvcConfigurer {

    private final List<WebMvcConfigurer> delegates = new ArrayList<>();


    public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.delegates.addAll(configurers);
        }
    }

    ...
}

参考 https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java

従って、アノテーション@EnableWebMvc@Configurationクラスのいずれか1つ(@Configurationが付与されるクラスは複数存在し得るため)に付与されており、クラスパス上に@Configurationを付与したWebMvcConfigurerインターフェース実装のクラスを設置する事で、コンフィグレーションのカスタマイズができるようになっているようです。

参考 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

package com.example.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@EnableWebMvc
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // Do something
        System.out.println(configurer);
    }

    ...
}

最新のSpring(5.0)より前のバージョンでは、WebMvcConfigurerAdapterクラスを継承していたようですが、5.0以降ではInterfaceのdefault実装を使用しているため、直接WebMvcConfigurerインターフェースを実装したクラスを定義できるようになっています。

Spring BootのWebMvcコンフィグレーション

Spring Bootのautoconfigureライブラリでは、

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfigurationが、@EnableWebMvc相当の処理を担当しているようです。

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    ...
    @Configuration
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter
            implements WebMvcConfigurer, ResourceLoaderAware {
        ...
    }

    /**
     * Configuration equivalent to {@code @EnableWebMvc}.
     */
    @Configuration
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
        ...
    }

    ...

}

WebMvcAutoConfigurationAdapterクラスで、EnableWebMvcConfigurationクラスがインポートされ、EnableWebMvcConfigurationクラスは内部クラスとして定義されています。そして、EnableWebMvcConfigurationクラスはDelegatingWebMvcConfigurationクラスを継承しているため、DelegatingWebMvcConfigurationクラスのように振る舞います。

このことから、Spring Bootでは@EnableWebMvcを明示的に指定しなくとも、@Configurationを付与したWebMvcConfigurerインターフェースを実装したクラスをコンフィグレーションクラスとしてクラスパス上に配置していれば良いようです。WebMvcConfigurationSupportクラスを継承したクラスでコンフィグレーションを記述した場合は、EnableWebMvcConfigurationクラスはインポートされません。SpringのDocsにもあるように、WebMvcConfigurerインターフェースによるカスタマイズを使用しない場合は、WebMvcConfigurationSupportクラスかDelegatingWebMvcConfigurationクラスを直接継承し、コンフィグレーションを記述することになるようです。

If WebMvcConfigurer does not expose some more advanced setting that needs to be configured consider removing the @EnableWebMvc annotation and extending directly from WebMvcConfigurationSupport or DelegatingWebMvcConfiguration, e.g.:

@Configuration
 @ComponentScan(basePackageClasses = { MyConfiguration.class })
 public class MyConfiguration extends WebMvcConfigurationSupport {

           @Override
           public void addFormatters(FormatterRegistry formatterRegistry) {
         formatterRegistry.addConverter(new MyConverter());
           }

           @Bean
           public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
         // Create or delegate to "super" to create and
         // customize properties of RequestMappingHandlerAdapter
           }
 }

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

コンフィグレーションをカスタマイズしたい場合、通常はWebMvcConfigurerインターフェースを実装したコンフィグレーションクラスを定義すればよいと思います。デフォルトで、すぐにアプリケーション開発ができるような設定がなされた状態となっているからです。

参考リンク

参考書籍

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発
株式会社NTTデータ
翔泳社
売り上げランキング: 7,987

本書籍で一通り学習しましたが、ページ数が豊富で幅広くトピックが取り上げられているのでお勧めです。Java言語そのものの知識は必要なので、『パーフェクトJava』などで基礎は学習しておくと良いです。

改訂2版 パーフェクトJava
井上 誠一郎 永井 雅人
技術評論社
売り上げランキング: 210,028
パーフェクト Java EE
パーフェクト Java EE

posted with amazlet at 18.08.07
井上 誠一郎 槙 俊明 上妻 宜人 菊田 洋一
技術評論社
売り上げランキング: 147,078

 

byebyehaikikyou

日記やIT系関連のネタ、WordPressに関することなど様々な事柄を書き付けた雑記です。ITエンジニア経験があるのでプログラミングに関することなどが多いです。

シェアする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントする

Translate »