CLOVER🍀

That was when it all began.

Spring BootのAutoConfigurationを自分で作ってみる

Spring Bootの勉強として、自分でAutoConfigurationを作ってみようかなと思いまして。

AutoConfigurationの内容を読んだりはしますが、自分で作ってみた方がより理解が深まりますよね。

というわけで、お題を以下のように設定してみました。

  • Spring Boot 1.5.1.RELEASEを使用
  • Ehcache3のAutoConfigurationを作る(現Spring Bootにはない)
  • SpringのCache Abstractionには絡めない
  • 単純に、CacheManagerをAutoConfigurationするだけ
  • 作成したstarterを、別のプロジェクトで読み込んで動かしてみる

参考にしたのは、まずは以下のドキュメント。

44. Creating your own auto-configuration

そして、Spring BootのAutoConfiguration自体も参考にしています。

https://github.com/spring-projects/spring-boot/tree/v1.5.1.RELEASE/spring-boot-autoconfigure

というか、これが1番の教材ではないでしょうか…。

それでは、始めてみます。

Mavenプロジェクトを作る

まずは、Mavenプロジェクトを作成します。

以下に習うと、「autoconfigure」と「starter」という2つのモジュールを作るのがよいそうです。

Creating your own starter

Namingについては、「xxxxx-spring-boot-autoconfigure」とか「xxxxx--spring-boot-starter」みたいな名前に
するべきだそうです。サードパーティ製のものが、「spring-boot」で始めるのは避けるようにしましょう、と。

「autoconfigure」には起動に必要な設定、Bean定義などを実装し、「starger」は依存関係を管理するだけの
モジュールとします。

というわけで、Mavenプロジェクトは以下のようなマルチプロジェクト構成となります。

$ find ehcache3-spring-boot -maxdepth 2 | grep pom.xml
ehcache3-spring-boot/pom.xml
ehcache3-spring-boot/ehcache3-spring-boot-autoconfigure/pom.xml
ehcache3-spring-boot/ehcache3-spring-boot-starter/pom.xml

以降は、「ehcache3-spring-boot」ディレクトリ内で作業をします。

$ cd ehcache3-spring-boot

親となる「ehcache3-spring-boot」プロジェクトのpom.xmlは、こんな感じ。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>ehcache3-spring-boot</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <spring.boot.version>1.5.1.RELEASE</spring.boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.ehcache</groupId>
                <artifactId>ehcache</artifactId>
                <version>3.2.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
            </plugin>
        </plugins>
    </build>

    <modules>
        <module>ehcache3-spring-boot-autoconfigure</module>
        <module>ehcache3-spring-boot-starter</module>
    </modules>
</project>

今回は、dependencyManagementに必要な依存関係を定義しておきます。

以降、「autoconfigure」と「starter」を見ていきましょう。

autoconfigureの作成

まずはautoconfigureを作成します。

pom.xmlの定義

pom.xmlの定義は、こんな感じにしました。
ehcache3-spring-boot-autoconfigure/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ehcache3-spring-boot</artifactId>
        <groupId>org.littlewings</groupId>
        <version>0.0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ehcache3-spring-boot-autoconfigure</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

依存関係にはSpring Bootを使うのに最低限必要な「spring-boot-starter」を含め、Ehcache3はoptionalに設定。

あと、「spring-boot-configuration-processor」を足していますが、これは@ConfigurationPropertiesに対して
コード補完のためのメタデータを生成することができます。
※@makingさんにツッコミいただきました。ありがとうございます。

Appendix B. Configuration meta-data

参考)
Spring Bootの@ ConfigurationPropertiesで型安全なプロパティ設定

@ConfigurationPropertiesを使ったサンプルは後で出てきますが、こちらを使用するとPluggable Annotation Processing API
アノテーションからメタデータを生成し(META-INF/spring-configuration-metadata.jsonというファイルができます)、
IDEでコード補完を行うことができるようになります。

AutoConfigurationを作成する

以下の2つの内容に習い、AutoConfigurationを作成します。

Understanding auto-configured beans

Condition annotations

各種@Conditinalなアノテーションを付与した@Configurationなクラスを作成し、Beanの定義をしていきます。

@Conditionalアノテーション自体は、各種@Conditionalなアノテーションに付与するアノテーションみたいですね。
以下のような各種条件で、設定が可能なようです。

  • Class conditions(@ConditionalOnClass、@ConditionalOnMissingClass) … クラスパス上に指定のクラスの有無
  • Bean conditions(@ConditionalOnBean、@ConditionalOnMissingBean) … ApplicationContextへのBean定義の有無
  • Property conditions(@ConditionalOnProperty) … プロパティ定義の有無
  • Resource conditions(@ConditionalOnResource) … リソースファイルの有無
  • Web application conditions(@ConditionalOnWebApplication、@ConditionalOnNotWebApplication) … Webアプリケーションかどうか
  • SpEL expression conditions(@ConditionalOnExpression) … SpELによる条件

今回は、シンプルにいってみます。こんなクラスを作成。
ehcache3-spring-boot-autoconfigure/src/main/java/org/littlewings/spring/boot/ehcache3/autoconfigure/Ehcache3AutoConfiguration.java

package org.littlewings.spring.boot.ehcache3.autoconfigure;

import java.io.IOException;
import java.net.URL;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expirations;
import org.ehcache.xml.XmlConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnMissingBean(CacheManager.class)
@EnableConfigurationProperties(Ehcache3Properties.class)
public class Ehcache3AutoConfiguration {
    @Bean(destroyMethod = "close")
    public CacheManager ehcache3CacheManager(Ehcache3Properties ehcache3Properties) throws IOException {
        if (ehcache3Properties.getConfig() != null) {
            // Ehcache3の設定ファイルがある場合
            URL configUrl = ehcache3Properties.getConfig().getURL();
            org.ehcache.config.Configuration configuration = new XmlConfiguration(configUrl);
            CacheManager cacheManager = CacheManagerBuilder.newCacheManager(configuration);
            cacheManager.init();
            return cacheManager;
        } else {
            // 設定からCacheManager作成
            Set<String> cacheNames = ehcache3Properties.getCacheNames();
            Long heapEntries = ehcache3Properties.getHeapEntries();
            Integer timeToLiveExpirationSeconds = ehcache3Properties.getTimeToLiveExpirationSeconds();
            Integer timeToIdleExpirationSeconds = ehcache3Properties.getTimeToIdleExpirationSeconds();

            CacheManagerBuilder<CacheManager> cacheManagerBuilder =
                    CacheManagerBuilder
                            .newCacheManagerBuilder();

            if (cacheNames != null) {
                for (String cacheName : cacheNames) {
                    CacheConfigurationBuilder<Object, Object> cacheConfigurationBuilder =
                            CacheConfigurationBuilder
                                    .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(heapEntries));

                    // ttl
                    if (timeToLiveExpirationSeconds != null) {
                        cacheConfigurationBuilder =
                                cacheConfigurationBuilder.withExpiry(Expirations.timeToLiveExpiration(Duration.of(timeToLiveExpirationSeconds, TimeUnit.SECONDS)));
                    }

                    // idle timeout
                    if (timeToIdleExpirationSeconds != null) {
                        cacheConfigurationBuilder =
                                cacheConfigurationBuilder.withExpiry(Expirations.timeToIdleExpiration(Duration.of(timeToIdleExpirationSeconds, TimeUnit.SECONDS)));
                    }

                    cacheManagerBuilder = cacheManagerBuilder.withCache(cacheName, cacheConfigurationBuilder.build());
                }
            }

            CacheManager cacheManager = cacheManagerBuilder.build();
            cacheManager.init();
            return cacheManager;
        }
    }
}

@Configurationを付与したクラスを作成して、

@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnMissingBean(CacheManager.class)
@EnableConfigurationProperties(Ehcache3Properties.class)
public class Ehcache3AutoConfiguration {

CacheManagerがクラスパス上にあり

@ConditionalOnClass(CacheManager.class)

CacheManagerがBean定義になければ有効ですと。

@ConditionalOnMissingBean(CacheManager.class)

あと、設定情報としてEhcache3Propertiesというクラスを使用するようにします。

@EnableConfigurationProperties(Ehcache3Properties.class)

Ehcache3Propertiesクラスの定義は、こんな感じです。
ehcache3-spring-boot-autoconfigure/src/main/java/org/littlewings/spring/boot/ehcache3/autoconfigure/Ehcache3Properties.java

package org.littlewings.spring.boot.ehcache3.autoconfigure;

import java.util.Set;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;

import static org.littlewings.spring.boot.ehcache3.autoconfigure.Ehcache3Properties.EHCACHE3_PREFIX;

@ConfigurationProperties(prefix = EHCACHE3_PREFIX)
public class Ehcache3Properties {
    public static final String EHCACHE3_PREFIX = "ehcache3";

    private Set<String> cacheNames;

    private Integer timeToLiveExpirationSeconds;

    private Integer timeToIdleExpirationSeconds;

    private Long heapEntries;

    private Resource config;

    // getter/setterは省略
}

これで、application.propertiesにこんな感じとか(これは設定ファイルの指定)

ehcache3.config=ehcache3.xml

こんな感じに書けるようになります。

ehcache3.cache-names=fooCache,barCache
ehcache3.heap-entries=10
ehcache3.time-to-live-expiration-seconds=3

これを使って、Ehcache3の設定ファイルが指定されていればそれを使い、そうでなければその他の
プロパティからCacheManagerとCacheを定義するようにしています。

    @Bean(destroyMethod = "close")
    public CacheManager ehcache3CacheManager(Ehcache3Properties ehcache3Properties) throws IOException {
        if (ehcache3Properties.getConfig() != null) {
            // Ehcache3の設定ファイルがある場合
            URL configUrl = ehcache3Properties.getConfig().getURL();
            org.ehcache.config.Configuration configuration = new XmlConfiguration(configUrl);
            CacheManager cacheManager = CacheManagerBuilder.newCacheManager(configuration);
            cacheManager.init();
            return cacheManager;
        } else {
            // 設定からCacheManager作成
            Set<String> cacheNames = ehcache3Properties.getCacheNames();
            Long heapEntries = ehcache3Properties.getHeapEntries();
            Integer timeToLiveExpirationSeconds = ehcache3Properties.getTimeToLiveExpirationSeconds();
            Integer timeToIdleExpirationSeconds = ehcache3Properties.getTimeToIdleExpirationSeconds();

            CacheManagerBuilder<CacheManager> cacheManagerBuilder =
                    CacheManagerBuilder
                            .newCacheManagerBuilder();

            if (cacheNames != null) {
                for (String cacheName : cacheNames) {
                    CacheConfigurationBuilder<Object, Object> cacheConfigurationBuilder =
                            CacheConfigurationBuilder
                                    .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(heapEntries));

                    // ttl
                    if (timeToLiveExpirationSeconds != null) {
                        cacheConfigurationBuilder =
                                cacheConfigurationBuilder.withExpiry(Expirations.timeToLiveExpiration(Duration.of(timeToLiveExpirationSeconds, TimeUnit.SECONDS)));
                    }

                    // idle timeout
                    if (timeToIdleExpirationSeconds != null) {
                        cacheConfigurationBuilder =
                                cacheConfigurationBuilder.withExpiry(Expirations.timeToIdleExpiration(Duration.of(timeToIdleExpirationSeconds, TimeUnit.SECONDS)));
                    }

                    cacheManagerBuilder = cacheManagerBuilder.withCache(cacheName, cacheConfigurationBuilder.build());
                }
            }

            CacheManager cacheManager = cacheManagerBuilder.build();
            cacheManager.init();
            return cacheManager;
        }
    }

@ConditionalOnMissingBeanなどは今回クラス定義に付与していますが、もうちょっと別のBean定義も制御したいなどと
いった場合は、メソッドにも付与することができます。Spring Boot自身のAutoConfigurationでも多用されているので、
確認してみるとよいでしょう。
※これでもCacheManagerがBean定義されていなかったら、という意味になります

    @Bean(destroyMethod = "close")
    @ConditionalOnMissingBean
    public CacheManager ehcache3CacheManager(Ehcache3Properties ehcache3Properties) throws IOException {
spring.factoriesの作成

META-INFディレクトリに、spring.factoriesというファイルを作成します。EnableAutoConfigurationに対する指定として、
先ほど作成した@Configurationなクラスを指定すればOKです。
ehcache3-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.littlewings.spring.boot.ehcache3.autoconfigure.Ehcache3AutoConfiguration

ドキュメントに記載のあるように、複数ある場合は「,」区切りで記述すればよさそうです。

Locating auto-configuration candidates

starterを作成する

starterはpom.xmlのみで、依存関係を定義したものだけを作成します。
ehcache3-spring-boot-starter/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ehcache3-spring-boot</artifactId>
        <groupId>org.littlewings</groupId>
        <version>0.0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ehcache3-spring-boot-starter</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.littlewings</groupId>
            <artifactId>ehcache3-spring-boot-autoconfigure</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
    </dependencies>
</project>

とりあえず、「mvn install」

このできあがったAutoConfigurationを別のプロジェクトから使うために、「mvn install」しておきます。

$ mvn install

Mavenリポジトリがある場合は、「mvn deploy」でもよいでしょう。

使ってみる

それでは、この作成したAutoConfigurationを使ったプロジェクトを作成してみます。

用意したpom.xmlは、こんな感じ。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>ehcache3-spring-boot-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <spring.boot.version>1.5.1.RELEASE</spring.boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.littlewings</groupId>
            <artifactId>ehcache3-spring-boot-starter</artifactId>
            <version>0.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

先ほど作成したAutoConfigurationを指定します。

        <dependency>
            <groupId>org.littlewings</groupId>
            <artifactId>ehcache3-spring-boot-starter</artifactId>
            <version>0.0.1</version>
        </dependency>

これで、作成したAutoConfigurationとEhcache3が依存関係に引き込まれます。

起動用のクラス作成。
src/main/java/demo/App.java

package demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

設定も、簡単にしてみます。
src/main/resources/application.properties

ehcache3.cache-names=fooCache,barCache
ehcache3.heap-entries=10
ehcache3.time-to-live-expiration-seconds=3

確認は、テストコードで行ってみましょう。こんな感じで作成。
src/test/java/demo/DemoAutoConfigurationTest.java

package demo;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoAutoConfigurationTest {
    @Autowired
    CacheManager cacheManager;

    @Test
    public void gettingStarted() {
        assertThat(cacheManager.getCache("fooCache", Object.class, Object.class))
                .isNotNull();
        assertThat(cacheManager.getCache("barCache", Object.class, Object.class))
                .isNotNull();

        assertThat(cacheManager.getCache("hogeCache", Object.class, Object.class))
                .isNull();
    }

    @Test
    public void expire() throws InterruptedException {
        Cache<Object, Object> cache = cacheManager.getCache("fooCache", Object.class, Object.class);

        cache.put("key", "value");
        assertThat(cache.get("key"))
                .isEqualTo("value");

        TimeUnit.SECONDS.sleep(5L);

        assertThat(cache.get("key"))
                .isNull();

        IntStream.rangeClosed(1, 20).forEach(i -> cache.put("key" + i, "value" + i));

        long size = StreamSupport
                .stream(cache.spliterator(), false)
                .count();
        assertThat(size)
                .isEqualTo(10L);
    }
}

これで動作します。AutoConfigurationで作成されたCacheManagerがインジェクション可能になっています。

    @Autowired
    CacheManager cacheManager;

次に、Ehcache3の設定ファイルベースに切り替えてみましょう。
src/main/resources/application.properties

ehcache3.config=ehcache3.xml

Ehcache3の設定は、こんな感じ。先ほどとはCacheの名前を変えておきます。
src/main/resources/ehcache3.xml

<?xml version="1.0" encoding="UTF-8"?>
<config
        xmlns='http://www.ehcache.org/v3'>
    <cache-template name="configureCacheTemplate">
        <key-type>java.lang.Object</key-type>
        <value-type>java.lang.Object</value-type>
        <expiry>
            <tti unit="seconds">5</tti>
        </expiry>
        <resources>
            <heap unit="entries">10</heap>
        </resources>
    </cache-template>

    <cache alias="configureCache" uses-template="configureCacheTemplate"/>
</config>

テストコードは、こんな感じのものに変更。

    @Test
    public void configureCache() throws InterruptedException {
        Cache<Object, Object> cache = cacheManager.getCache("configureCache", Object.class, Object.class);

        cache.put("key", "value");
        assertThat(cache.get("key"))
                .isEqualTo("value");

        TimeUnit.SECONDS.sleep(5L);

        assertThat(cache.get("key"))
                .isNull();

        IntStream.rangeClosed(1, 20).forEach(i -> cache.put("key" + i, "value" + i));

        long size = StreamSupport
                .stream(cache.spliterator(), false)
                .count();
        assertThat(size)
                .isEqualTo(10L);
    }

これでも動作します。

最後は、自分でCacheManagerを自分でBean定義してAutoConfigurationの内容を使わないようにしてみます。

起動クラスに、Bean定義を含めてみましょう。
src/main/java/demo/App.java

package demo;

import java.util.concurrent.TimeUnit;

import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expirations;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

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

    @Bean(destroyMethod = "close")
    public CacheManager cacheManager() {
        CacheManagerBuilder<CacheManager> cacheManagerBuilder =
                CacheManagerBuilder
                        .newCacheManagerBuilder();

        CacheConfigurationBuilder<Object, Object> cacheConfigurationBuilder =
                CacheConfigurationBuilder
                        .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(10));

        // ttl
        cacheConfigurationBuilder =
                cacheConfigurationBuilder.withExpiry(Expirations.timeToLiveExpiration(Duration.of(3, TimeUnit.SECONDS)));

        cacheManagerBuilder = cacheManagerBuilder.withCache("myCache", cacheConfigurationBuilder.build());

        CacheManager cacheManager = cacheManagerBuilder.build();
        cacheManager.init();
        return cacheManager;
    }
}

今度は、「myCache」にしてみました。

        cacheManagerBuilder = cacheManagerBuilder.withCache("myCache", cacheConfigurationBuilder.build());

テストコードはこんな感じで。

    @Test
    public void myCache() throws InterruptedException {
        Cache<Object, Object> cache = cacheManager.getCache("myCache", Object.class, Object.class);

        cache.put("key", "value");
        assertThat(cache.get("key"))
                .isEqualTo("value");

        TimeUnit.SECONDS.sleep(5L);

        assertThat(cache.get("key"))
                .isNull();

        IntStream.rangeClosed(1, 20).forEach(i -> cache.put("key" + i, "value" + i));

        long size = StreamSupport
                .stream(cache.spliterator(), false)
                .count();
        assertThat(size)
                .isEqualTo(10L);
    }

AutoConfigurationで定義されたBeanではなく、アプリケーション側でBean定義されたCacheManagerが使用されたことが
確認できました。

とりあえず、こんな感じで動かすことができましたよ、と。

まとめ

簡単にではありますが、Spring BootのAutoConfigurationを自分でも作ってみました。

極めて初歩的な内容だと思いますが、あとはSpring BootのAutoConfigurationのソースコードを見たりしてパターンを覚えて
いけばいいのかな?と思います。

依存関係とかをもうちょっとまとめたい場合は、BOMを作った方がいいのかもしれません。
Spring Bootがそもそもこのスタイルですね。

https://github.com/spring-projects/spring-boot/tree/v1.5.1.RELEASE/spring-boot-dependencies