Springフレームワークでは、依存性注入(DI)が標準的なBean管理手法ですが、特定のユースケースでは、コード中で動的にBeanを取得する必要があります。たとえば、汎用ユーティリティクラス、ファクトリパターンの実装、または非Spring管理のスレッド内でのBean利用などです。本稿では、ApplicationContextを基盤とした7つの安全かつ保守性の高いBean取得方法を紹介し、各手法の適用場面と注意点を明確に解説します。
ApplicationContext:Springアプリケーションの中枢
SpringのIoCコンテナは、BeanFactoryを基盤として構築されますが、実際のアプリケーション開発では、その拡張インターフェースであるApplicationContextが主に使用されます。これは、国際化(i18n)、イベント発行、AOP統合、および初期化時のBean検証など、実運用に必要な機能を提供するためです。また、ApplicationContextはシングルトンBeanを起動時に即時インスタンス化するため、設定ミスを早期に検出できます。
Bean取得の実践的手法
1. ApplicationContextAwareによる静的保持(推奨)
最もシンプルで広く採用される手法です。Springがコンポーネント初期化時にApplicationContextを自動注入し、それを静的フィールドに保持します。
@Component
public class ContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
public static <T> T resolveBean(Class<T> type) {
return context.getBean(type);
}
public static <T> T resolveBean(String name, Class<T> type) {
return context.getBean(name, type);
}
}
2. ApplicationRunnerによる起動時初期化
Spring Boot環境では、ApplicationRunnerを活用してコンテキストを安全にキャプチャできます。これにより、コンテキストが完全に初期化された後に処理が実行されます。
@Component
public class ContextInitializer implements ApplicationRunner {
private static ApplicationContext appContext;
@Override
public void run(ApplicationArguments args) {
appContext = SpringApplication.run(YourApp.class, args).getApplicationContext();
}
public static <T> T getBean(Class<T> clazz) {
return appContext.getBean(clazz);
}
}
3. 自作ヘルパークラス+@PostConstruct
コンポーネントとして登録されたヘルパークラス内で、@PostConstructを使用してコンテキスト参照を確立します。静的メソッド経由でアクセス可能にします。
@Component
public class BeanResolver {
private static ApplicationContext ctx;
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
ctx = applicationContext;
}
@PostConstruct
public void initialize() {
// 必要に応じて初期化ロジック
}
public static <T> T of(Class<T> type) {
return ctx.getBean(type);
}
}
4. WebApplicationContextUtils(Webアプリケーション向け)
ServletベースのWebアプリケーションでは、ServletContextから直接WebApplicationContextを取得できます。特にFilterやListener内で有用です。
public class WebBeanProvider {
public static <T> T fetchFromContext(ServletContext servletContext, String beanName, Class<T> type) {
WebApplicationContext wac =
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
return wac.getBean(beanName, type);
}
}
5. ConfigurableApplicationContextの直接利用(テスト・マイグレーション用途)
Spring BootのSpringApplication.run()はConfigurableApplicationContextを返すため、起動直後に一時的に保持して利用可能です。ただし、長期間の保持は非推奨です。
public class Bootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
MyService service = context.getBean(MyService.class);
service.execute();
// context.close(); // 明示的クローズが必要な場合はここで
}
}
6. Lookup Method Injection(動的プロトタイプBean取得)
プロトタイプスコープのBeanを毎回新しいインスタンスで取得したい場合、XML設定ではなく、@Lookupアノテーションを用いたメソッド注入が推奨されます。
@Component
public abstract class PrototypeFactory {
@Lookup
public abstract RequestScopedProcessor createProcessor();
}
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RequestScopedProcessor {
// プロトタイプBeanの実装
}
7. Spring Boot ActuatorのHealthIndicator連携(監視・診断用途)
運用監視目的でBeanの状態を確認する場合、ActuatorのHealthIndicatorを活用し、内部で必要なBeanを取得・検証できます。
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
public DatabaseHealthIndicator(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Health health() {
try (Connection conn = dataSource.getConnection()) {
return Health.up().withDetail("database", "connected").build();
} catch (SQLException e) {
return Health.down().withDetail("error", e.getMessage()).build();
}
}
}
選択の指針
- 汎用ユーティリティクラス →
ContextHolder(ApplicationContextAware) - Web層での動的取得 →
WebApplicationContextUtils - プロトタイプBeanの生成 →
@Lookupメソッド注入 - 起動時のワンタイム処理 →
ApplicationRunnerまたはCommandLineRunner - テスト/マイグレーション支援 →
ConfigurableApplicationContextの直接保持(一時的)
なお、BeanFactory単体の利用やXmlBeanFactoryのような非推奨APIは、現代のSpring Bootプロジェクトでは避けるべきです。すべての手法は、ApplicationContextという共通の抽象レイヤーを基盤としており、その本質は「IoCコンテナへのアクセス」に集約されます。