SpringアプリケーションコンテキストからBeanを取得する7つの実践的アプローチ

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コンテナへのアクセス」に集約されます。

タグ: spring-boot ApplicationContext dependency-injection ioc-container spring-framework

6月28日 21:38 投稿