Springの起動時にロジックを実行するためのガイド
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の初期化の直後に
@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を完全に制御するために、上記のメカニズムを組み合わせることができます。
実行順序は次のとおりです。
- コンストラクタ
- @PostConstruct注釈付きメソッド
- InitializingBeanのafterPropertiesSet()メソッド
- 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のにあります。