Spring Bootの@ServletComponentScanアノテーション

Spring Bootの@ServletComponentScanアノテーション

1. 概要

この記事では、Spring Boot.の新しい@ServletComponentScanアノテーションについて説明します。

目的は、次のServlet 3.0アノテーションをサポートすることです。

  • javax.servlet.annotation.WebFilter

  • javax.servlet.annotation.WebListener

  • javax.servlet.annotation.WebServlet

@WebServlet@WebFilter、および@WebListenerアノテーション付きクラスは、@Configurationクラスに@ServletComponentScanアノテーションを付けて指定することにより、埋め込みServletコンテナーに自動的に登録できます。パッケージ。

Introduction to Java Servlets@WebServletIntroduction to Intercepting Filter Pattern in Java@WebFilterの基本的な使用法を紹介しました。 @WebListenerの場合、Webリスナーの一般的な使用例を示すthis articleを確認できます。

2. ServletsFilters、およびListeners

@ServletComponentScanに飛び込む前に、@ServletComponentScanが機能する前に、アノテーション(@WebServlet@WebFilter@WebListener)がどのように使用されたかを見てみましょう。

2.1. @WebServlet

次に、最初にGETリクエストを処理し、“hello”に応答するServletを定義します。

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        try {
            response
              .getOutputStream()
              .write("hello");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

2.2. @WebFilter

次に、ターゲット“/hello”への要求をフィルタリングし、出力の前に“filtering “を追加するフィルター:

@WebFilter("/hello")
public class HelloFilter implements Filter {

    //...
    @Override
    public void doFilter(
      ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
        servletResponse
          .getOutputStream()
          .print("filtering ");
        filterChain.doFilter(servletRequest, servletResponse);
    }
    //...

}

2.3. @WebListener

最後に、ServletContextにカスタム属性を設定するリスナー:

@WebListener
public class AttrListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        servletContextEvent
          .getServletContext()
          .setAttribute("servlet-context-attr", "test");
    }
    //...
}

2.4. Servletコンテナにデプロイする

単純なWebアプリケーションの基本コンポーネントを構築したので、それをパッケージ化してServletコンテナーにデプロイできます。 パッケージ化されたwarファイルをJettyTomcat、またはServlet 3.0をサポートする任意のServletコンテナにデプロイすることで、各コンポーネントの動作を簡単に確認できます。

3. Spring Boot@ServletComponentScanを使用する

ほとんどのServletコンテナーでこれらの注釈を構成なしで使用できるので、なぜ@ServletComponentScanが必要なのか疑問に思われるかもしれません。 問題は、埋め込まれたServletコンテナにあります。

埋め込みコンテナは@WebServlet@WebFilter、および@WebListenerアノテーションをサポートしていないため、Spring Boot,は埋め込みコンテナに大きく依存しており、この新しいアノテーション@ServletComponentScanをこれらの3つのアノテーションを使用するいくつかの依存jarをサポートします。

詳細な説明はthis issue on Githubにあります。

3.1. Mavenの依存関係

@ServletComponentScanを使用するには、バージョン1.3.0以降のSpring Bootが必要です。 最新バージョンのspring-boot-starter-parentspring-boot-starter-webpomに追加しましょう。


    org.springframework.boot
    spring-boot-starter-parent
    1.5.1.RELEASE
     

    
        org.springframework.boot
        spring-boot-starter-web
        1.5.1.RELEASE
    

3.2. @ServletComponentScanの使用

Spring Bootアプリは非常にシンプルです。 @ServletComponentScanを追加して、@WebFilter@WebListener、および@WebServlet:のスキャンを有効にします

@ServletComponentScan
@SpringBootApplication
public class SpringBootAnnotatedApp {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAnnotatedApp.class, args);
    }

}

以前のWebアプリケーションに変更を加えなくても、そのまま機能します。

@Autowired private TestRestTemplate restTemplate;

@Test
public void givenServletFilter_whenGetHello_thenRequestFiltered() {

    ResponseEntity responseEntity =
      restTemplate.getForEntity("/hello", String.class);

    assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
    assertEquals("filtering hello", responseEntity.getBody());
}
@Autowired private ServletContext servletContext;

@Test
public void givenServletContext_whenAccessAttrs_thenFoundAttrsPutInServletListner() {

    assertNotNull(servletContext);
    assertNotNull(servletContext.getAttribute("servlet-context-attr"));
    assertEquals("test", servletContext.getAttribute("servlet-context-attr"));
}

3.3. スキャンするパッケージを指定する

デフォルトでは、@ServletComponentScanはアノテーション付きクラスのパッケージからスキャンします。 スキャンするパッケージを指定するには、その属性を使用できます。

  • basePackages

  • basePackageClasses

デフォルトのvalue属性は、basePackagesのエイリアスです。

SpringBootAnnotatedAppがパッケージcom.example.annotationの下にあり、上記のWebアプリケーションで作成されたパッケージcom.example.annotation.componentsのクラスをスキャンするとします。次の構成は、同等です。

@ServletComponentScan
@ServletComponentScan("com.example.annotation.components")
@ServletComponentScan(basePackages = "com.example.annotation.components")
@ServletComponentScan(
  basePackageClasses =
    {AttrListener.class, HelloFilter.class, HelloServlet.class})

4. フードの下

@ServletComponentScanアノテーションはServletComponentRegisteringPostProcessorによって処理されます。 指定されたパッケージで@WebFilter@WebListener、および@WebServletアノテーションをスキャンした後、ServletComponentHandlersのリストがそれらのアノテーション属性を処理し、スキャンされたBeanを登録します。

class ServletComponentRegisteringPostProcessor
  implements BeanFactoryPostProcessor, ApplicationContextAware {

    private static final List HANDLERS;

    static {
        List handlers = new ArrayList<>();
        handlers.add(new WebServletHandler());
        handlers.add(new WebFilterHandler());
        handlers.add(new WebListenerHandler());
        HANDLERS = Collections.unmodifiableList(handlers);
    }

    //...

    private void scanPackage(
      ClassPathScanningCandidateComponentProvider componentProvider,
      String packageToScan){
        //...
        for (ServletComponentHandler handler : HANDLERS) {
            handler.handle(((ScannedGenericBeanDefinition) candidate),
              (BeanDefinitionRegistry) this.applicationContext);
        }
    }
}

official Javadoc@ServletComponentScan annotation only works in embedded Servlet containersで述べたように、これはデフォルトでSpring Bootに付属しています。

5. 結論

この記事では、@ServletComponentScanと、それを使用して、注釈(@WebServlet@WebFilter@WebListener)のいずれかに依存するアプリケーションをサポートする方法を紹介しました。

例とコードの実装はin the GitHub projectにあります。