【SpringBoot】非推奨なpropertyを使用していたらビルドを失敗させる

# 概要

非推奨なpropertyの検知のために`spring-boot-properties-migrator`という便利なライブラリが用意されていますが、できればビルドを失敗させたい…！

→ 少し強引な方法ですが、ひとまず実現できたのでメモ代わりに残しておきます

# `spring-boot-properties-migrator`とは？

非推奨なpropertyの検知して、warnログやerrorログでお知らせしてくれるライブラリです。
（propertyの読み替えもやってくれているはず）

主に`PropertiesMigrationListener`が頑張ってくれています。

# 準備

この記事を書いた際に手元で動かしていたソースのbuild.gradleの抜粋はこちらです

```groovy:build.gradle
plugins {
    id 'org.springframework.boot' version "2.5.0"
    id 'io.spring.dependency-management' version "1.0.11.RELEASE"
    id 'java'
    id 'com.github.ben-manes.versions' version "0.36.0"
}

...

dependencies {
    ...
    testRuntimeOnly("org.springframework.boot:spring-boot-properties-migrator")
    testCompileOnly("org.springframework.boot:spring-boot-properties-migrator")
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
```


# ビルドを失敗させる

`PropertiesMigrationListener`なのですが、ログ出力のみでレポートの内容を取得するようなメソッドは用意されていないので、自分でwrapperクラスを作ってあげます。

その実装が↓こちら↓

```java:org.springframework.boot.context.properties.migrator.PropertiesMigrationListenerWrapper
@Slf4j
public class PropertiesMigrationListenerWrapper
        implements ApplicationListener<SpringApplicationEvent> {

    private PropertiesMigrationReport report;

    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        log.info("event class: {}", event.getClass());
        if (event instanceof ApplicationStartedEvent) {
            onApplicationPreparedEvent((ApplicationStartedEvent) event);
        }
    }

    @EventListener
    public void onApplicationPreparedEvent(ApplicationStartedEvent event) {
        ConfigurationMetadataRepository repository = loadRepository();
        PropertiesMigrationReporter reporter = new PropertiesMigrationReporter(repository,
                event.getApplicationContext().getEnvironment());
        this.report = reporter.getReport();
    }

    private ConfigurationMetadataRepository loadRepository() {
        try {
            return loadRepository(ConfigurationMetadataRepositoryJsonBuilder.create());
        }
        catch (IOException ex) {
            throw new IllegalStateException("Failed to load metadata", ex);
        }
    }

    private ConfigurationMetadataRepository loadRepository(ConfigurationMetadataRepositoryJsonBuilder builder)
            throws IOException {
        Resource[] resources = new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/META-INF/spring-configuration-metadata.json");
        for (Resource resource : resources) {
            try (InputStream inputStream = resource.getInputStream()) {
                builder.withJsonResource(inputStream);
            }
        }
        return builder.build();
    }

    // 非推奨なpropertyが存在するか真偽値を返す
    public boolean existsDeprecatedProperty() {
        var result = false;
        String warningReport = this.report.getWarningReport();
        if (warningReport != null) {
            log.warn(warningReport);
            result = true;
        }

        String errorReport = this.report.getErrorReport();
        if (errorReport != null) {
            log.error(errorReport);
            result = false;
        }

        return result;
    }
}
```

ほぼ`PropertiesMigrationListener`のコピペなのですが、テストの検証用に`existsDeprecatedProperty()`を用意しています。

また、packageは`org.springframework.boot.context.properties.migrator`固定です。
（利用クラスがpackage privateなクラスなので…）
はい、これが強引と言ったいた理由ですmm


Wrapperクラスは用意できたので、SpringApplicationEventをlistenしてくれるようにBean登録しておきます。

```java
@Configuration
public class TestAutoConfiguration {
    @Bean
    public PropertiesMigrationListenerWrapper propertiesMigrationListenerWrapper() {
        return new PropertiesMigrationListenerWrapper();
    }
}
```

最後にテストクラスです。

```java
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {Application.class, TestAutoConfiguration.class})
class PropertyMigrateTest {

    @Autowired
    private PropertiesMigrationListenerWrapper migrationListener;

    @Test
    void test() {
        assertFalse(migrationListener.existsDeprecatedProperty());
    }
}
```