SpringBootの@ServletComponentScanアノテーション
1. 概要
この記事では、Spring Bootの新しい@ServletComponentScanアノテーションについて説明します。
目的は、次のサーブレット3.0アノテーションをサポートすることです。
- javax.servlet.annotation.WebFilter
- javax.servlet.annotation.WebListener
- javax.servlet.annotation.WebServlet
@WebServlet 、 @WebFilter 、および @WebListener アノテーション付きクラスは、 @アノテーションを付けることにより、埋め込まれたサーブレットコンテナーに自動的に登録できます。 @ConfigurationクラスのServletComponentScanとパッケージの指定。
Javaサーブレットの概要で@WebServletの基本的な使用法を紹介し、Javaでのインターセプトフィルターパターンの概要で@WebFilterを紹介しました。 @WebListener については、Webリスナーの一般的な使用例を示すこの記事をご覧ください。
2. サーブレット、フィルター、およびリスナー
@ServletComponentScan に飛び込む前に、アノテーション @WebServlet 、 @WebFilter 、@WebListenerが以前にどのように使用されていたかを見てみましょう。 @ServletComponentScanが登場しました。
2.1. @WebServlet
次に、 GET 要求を処理し、“ hello”に応答するサーブレットを最初に定義します。
@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. サーブレットコンテナにデプロイする
単純なWebアプリケーションの基本コンポーネントを構築したので、それをパッケージ化してサーブレットコンテナーにデプロイできます。 パッケージ化されたwarファイルをJetty、 Tomcat 、または Servlet3.0をサポートする任意のServletコンテナーにデプロイすることで、各コンポーネントの動作を簡単に確認できます。
3. Spring Bootで@ServletComponentScanを使用する
ほとんどのサーブレットコンテナでこれらのアノテーションを構成なしで使用できるので、なぜ @ServletComponentScanが必要なのか疑問に思われるかもしれません。 問題は、埋め込まれたサーブレットコンテナにあります。
埋め込みコンテナは@WebServlet、 @WebFilter 、 @WebListener アノテーションをサポートしていないため、 Spring Boot、は大きく依存しています埋め込みコンテナでは、この3つのアノテーションを使用するいくつかの依存jarをサポートするために、この新しいアノテーション@ServletComponentScanが導入されました。
詳細については、Githubのこの号をご覧ください。
3.1. Mavenの依存関係
@ServletComponentScan を使用するには、バージョン1.3.0以降の SpringBootが必要です。 spring-boot-starter-parentとspring-boot-starter-webの最新バージョンをpomに追加しましょう。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
3.2. @ServletComponentScanを使用する
SpringBootアプリはとてもシンプルです。 @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<String> 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.baeldung.annotationの下にあり、Webアプリケーションで作成されたパッケージcom.baeldung.annotation.componentsのクラスをスキャンするとします。上記では、次の構成は同等です。
@ServletComponentScan
@ServletComponentScan("com.baeldung.annotation.components")
@ServletComponentScan(basePackages = "com.baeldung.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<ServletComponentHandler> HANDLERS;
static {
List<ServletComponentHandler> 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);
}
}
}
公式Javadocで述べたように、 @ServletComponentScanアノテーションは、デフォルトで SpringBootに付属している組み込みサーブレットコンテナでのみ機能します。
5. 結論
この記事では、 @ServletComponentScan と、アノテーションのいずれかに依存するアプリケーションをサポートするためにそれを使用する方法を紹介しました: @WebServlet 、 @WebFilter 、[ X191X]@WebListener。
例とコードの実装は、GitHubプロジェクトのにあります。