Spring BootにおけるServletコンテキストリスナーの4つの登録パターン

プロジェクト環境構築

まず、Webアプリケーション開発に必要な依存関係を定義します。Spring Bootの親ページェントとウェブスタターを追加することで、内蔵Tomcatおよびサーブレットコンテナとの連携基盤が有効化されます。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
</parent>

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

コンテキストリスナーの登録手法

Java Web仕様に基づいた{@code ServletContextListener}をSpring Bootアプリケーションに組み込む場合、主に以下の4つの実装パターンがサポートされています。

1. アノテーション駆動型登録

Serlet 3.0以降で規定された{@code @WebListener}を活用する方法です。これはサーブレット標準仕様のため、Spring独自の自動検出対象外となります。{@code @ServletComponentScan}を設定することでパッケージスキャンを有効にし、リスナーとして認識させます。

@WebListener
public class AnnotationDrivenListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("[アノテーション] サーブレットコンテキスト起動を検知");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("[アノテーション] サーブレットコンテキスト終了を検知");
    }
}

アプリケーション起動クラス側にスキャンディレクティブを追加します:

@ServletComponentScan(basePackages = "com.example.listener")
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

2. Spring Beanによる自動ラップ

通常の{@code @Component}アノテーションを付与するだけで、Spring Bootの自動構成メカニズムが内部で{@code ServletListenerRegistrationBean}を生成し、サーブレットコンテナへ自動的にマッピングします。設定ファイルの記述が最小限で済むため、保守性が最も高い方式です。

@Component
public class ManagedBeanListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("[Spring Bean] 委譲型リスナーの初期化フェーズ");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("[Spring Bean] 委譲型リスナーのクリーンアップフェーズ");
    }
}

3. Java Configurationによる明示的定義

{@code @Configuration}クラス内で{@code ServletListenerRegistrationBean}インスタンスを{@code @Bean}として直接定義します。リスナー本体の実装と登録ロジックを完全に分離でき、外部プロパティ値に基づく条件付き登録や、{@code setOrder()}による実行順序の厳格な制御が可能です。

public class ExplicitDefinitionListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("[Java Config] 手動構成されたリスナー起動");
    }
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("[Java Config] 手動構成されたリスナー終了");
    }
}

対応する設定クラス:

@Configuration
public class ListenerRegistrarConfig {

    @Bean
    public ServletListenerRegistrationBean<ExplicitDefinitionListener> buildListenerBean() {
        ServletListenerRegistrationBean<ExplicitDefinitionListener> wrapper = new ServletListenerRegistrationBean<>();
        wrapper.setListener(new ExplicitDefinitionListener());
        wrapper.setOrder(Integer.MAX_VALUE); // デフォルトより低い優先度を明示
        return wrapper;
    }
}

4. Programmatic Initializerによるフック

{@code ServletContextInitializer}インターフェースを実装し、{@code onStartup}メソッド内でサーブレットAPIレベルで直接登録します。このアプローチはSpring Bootの起動パイプラインにおいて非常に早期に実行されるため、他の登録方式よりも優先順位が高くなります。フレームワークの初期化プロセスを完全にバイパスする純粋なServletコンテナ操作が必要な場合に使用されます。

public class HighPriorityHookListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("[イニシャライザ] 最優先登録リスナーの起動");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("[イニシャライザ] 最優先登録リスナーの停止");
    }
}

登録トリガーとなるコンポーネント:

@Component
public class BootstrapInitializer implements ServletContextInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // アプリケーションの標準マッピング前に直接登録
        servletContext.addListener(HighPriorityHookListener.class);
    }
}

いずれの手法もサーブレット仕様に準拠して正しく動作しますが、プロジェクト全体で一貫性を持たせるため通常は単一の登録スタイルに統一することが推奨されます。高度な順序制御や非Spring lifecycle依存の関係を除き、宣言型の{@code @WebListener}または{@code @Component}方式が標準的な実装として広く採用されています。

タグ: SpringBoot ServeltContextListener SpringJavaConfig SpringBootWeb ServletAPI

8月11日 11:01 投稿