kagamihogeの日記

kagamihogeの日記です。

EnvironmentPostProcessorでEnvironment初期化処理をカスタマイズ

spring-bootのEnvironmentPostProcessorを実装する事でEnvironmentの初期化処理に後処理を追加可能になる。これでプロパティの初期化をカスタマイズできる。大抵のケースでExternalized Configurationを利用すればプロパティは問題無いが、それでは機能不足する場合などに利用する。

以下の例では実行時に現在時刻(EnvironmentPostProcessor実行時点)のプロパティを追加する。

plugins {
    id 'org.springframework.boot' version '2.4.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '15'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}
import java.time.LocalDateTime;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;

@Order(Ordered.LOWEST_PRECEDENCE)
public class MyEnvPostProcessor implements EnvironmentPostProcessor {

    public MyEnvPostProcessor(DeferredLogFactory factory, Log log, ConfigurableBootstrapContext context) {
        log.info("log MyEnvPostProcessor");
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        PropertySource<?> property = new MapPropertySource("myEnvPost", Map.of("current.local-date-time", LocalDateTime.now().toString()));
        environment.getPropertySources().addLast(property);
    }

}

current.local-date-timeという名前のプロパティを追加している。

@Orderは任意だが用途を考えると基本的には低優先度を設定すると思われる。

また、任意でコンストラクタにはDeferredLogFactory, org.apache.commons.logging.Log, ConfigurableBootstrapContextを指定できる。

次にこの実装クラスをMETA-INF/spring.factoriesで登録する。src/main/resources/META-INF/spring.factoriesを作成する。

org.springframework.boot.env.EnvironmentPostProcessor=kagaimhoge.sample.MyEnvPostProcessor

最後に動作確認用のMainクラス。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    @Value("${current.local-date-time}")
    String now;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(now);
    }
}