1. 概要

このチュートリアルでは、Springアプリケーションの起動時にロジックを実行する方法に焦点を当てます。

2. 起動時にロジックを実行する

Springアプリケーションの起動中/起動後にロジックを実行するのが一般的なシナリオです。 しかし、それは複数の問題を引き起こすものでもあります。

制御の反転の恩恵を受けるには、コンテナへのアプリケーションのフローに対する部分的な制御を放棄する必要があります。 これが、インスタンス化、起動時のセットアップロジックなどの理由です。 特別な注意が必要です。

これらのプロセスの間は制御できないため、Beanのコンストラクターにロジックを含めたり、オブジェクトのインスタンス化後にメソッドを呼び出したりすることはできません。

実際の例を見てみましょう。

@Component
public class InvalidInitExampleBean {

    @Autowired
    private Environment env;

    public InvalidInitExampleBean() {
        env.getActiveProfiles();
    }
}

ここでは、コンストラクターのautowiredフィールドにアクセスしようとしています。 コンストラクターが呼び出されたとき、SpringBeanはまだ完全には初期化されていません。 まだ初期化されていないフィールドを呼び出すと、NullPointerExceptionsが発生するため、これは問題です。

Springがこの状況を管理するために私たちに与えるいくつかの方法を見てみましょう。

2.1. @PostConstructアノテーション

Javaxの@PostConstructアノテーションを使用して、Beanの初期化の直後に1回実行する必要があるメソッドにアノテーションを付けることができます。注入します。

@PostConstructの動作は次のとおりです。

@Component
public class PostConstructExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(PostConstructExampleBean.class);

    @Autowired
    private Environment environment;

    @PostConstruct
    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

Environment インスタンスが安全に注入され、 NullPointerException をスローせずに、@PostConstruct注釈付きメソッドで呼び出されたことがわかります。

2.2. InitializingBeanインターフェイス

InitializingBeanアプローチも同様に機能します。 メソッドにアノテーションを付ける代わりに、 InitializingBeanインターフェイスとafterPropertiesSet()メソッドを実装する必要があります。

ここでは、InitializingBeanインターフェイスを使用して前の例を実装します。

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG 
      = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.3. ApplicationListener

このアプローチは、Springコンテキストが初期化された後の実行ロジックに使用できます。したがって、特定のBeanに焦点を当てていません。 代わりに、それらすべてが初期化されるのを待っています。

これを行うには、を実装するBeanを作成する必要があります ApplicationListener インターフェース:

@Component
public class StartupApplicationListenerExample implements 
  ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOG 
      = Logger.getLogger(StartupApplicationListenerExample.class);

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

新しく導入された@EventListenerアノテーションを使用しても、同じ結果を得ることができます。

@Component
public class EventListenerExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(EventListenerExampleBean.class);

    public static int counter;

    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

ニーズに合ったイベントを必ず選んでいきたいと思います。 この例では、ContextRefreshedEventを選択しました。

2.4. @Bean initMethod属性

initMethod プロパティを使用して、Beanの初期化後にメソッドを実行できます。

Beanは次のようになります。

public class InitMethodExampleBean {

    private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

    @Autowired
    private Environment environment;

    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

特別なインターフェースを実装したり、特別なアノテーションを使用したりしていないことに注意してください。

次に、@Beanアノテーションを使用してBeanを定義できます。

@Bean(initMethod="init")
public InitMethodExampleBean initMethodExampleBean() {
    return new InitMethodExampleBean();
}

そして、これはXML構成でのBean定義の外観です。

<bean id="initMethodExampleBean"
  class="com.baeldung.startup.InitMethodExampleBean"
  init-method="init">
</bean>

2.5. コンストラクターインジェクション

コンストラクターインジェクションを使用してフィールドをインジェクションしている場合は、単純にロジックをコンストラクターに含めることができます。

@Component 
public class LogicInConstructorExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(LogicInConstructorExampleBean.class);

    private final Environment environment;

    @Autowired
    public LogicInConstructorExampleBean(Environment environment) {
        this.environment = environment;
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.6. Spring Boot CommandLineRunner

Spring Bootは、コールバック run()メソッドを備えたCommandLineRunnerインターフェースを提供します。 これは、Springアプリケーションコンテキストがインスタンス化された後、アプリケーションの起動時に呼び出すことができます。

例を見てみましょう:

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

    public static int counter;

    @Override
    public void run(String...args) throws Exception {
        LOG.info("Increment counter");
        counter++;
    }
}

ドキュメントに記載されているように、複数の CommandLineRunner Beanを同じアプリケーションコンテキスト内で定義でき、@Orderedを使用して注文できます。 ]インターフェイスまたは@Orderアノテーション。

2.7. Spring Boot ApplicationRunner

CommandLineRunner と同様に、Spring Bootは、アプリケーションの起動時に呼び出される run()メソッドを備えたApplicationRunnerインターフェイスも提供します。 ただし、コールバックメソッドに渡される生の String 引数の代わりに、ApplicationArgumentsクラスのインスタンスがあります。

ApplicationArguments インターフェイスには、オプションである引数値とプレーンな引数値を取得するメソッドがあります。 接頭辞– –が付いた引数は、オプションの引数です。

例を見てみましょう:

@Component
public class AppStartupRunner implements ApplicationRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(AppStartupRunner.class);

    public static int counter;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOG.info("Application started with option names : {}", 
          args.getOptionNames());
        LOG.info("Increment counter");
        counter++;
    }
}

3. メカニズムの組み合わせ

Beanを完全に制御するために、上記のメカニズムを組み合わせることができます。

実行順序は次のとおりです。

  1. コンストラクタ
  2. @PostConstruct注釈付きメソッド
  3. InitializingBeanのafterPropertiesSet()メソッド
  4. XMLでinit-methodとして指定された初期化メソッド

すべてのメカニズムを組み合わせたSpringBeanを作成しましょう。

@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {

    private static final Logger LOG 
      = Logger.getLogger(AllStrategiesExampleBean.class);

    public AllStrategiesExampleBean() {
        LOG.info("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.info("PostConstruct");
    }

    public void init() {
        LOG.info("init-method");
    }
}

このBeanをインスタンス化しようとすると、上記で指定した順序に一致するログが表示されます。

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method

4. 結論

この記事では、Springのアプリケーション起動時にロジックを実行する複数の方法を示しました。

コードサンプルは、GitHubにあります。