「Spring」サーブレット内で @Autowired を使ってインジェクションする

サーブレット内で @Autowired を使ってインジェクションしようと思ったんですが、なかなかはまったのでメモしておきます ( ただ、方法は見つかったんですが、最後まで理解できませんでした )。

最初は、以下みたいな感じで web.xml に ContextLoaderListener の設定して、サーブレット内で @Autowired するだけでいけるんかなと思ってました。

■ web.xml ( 抜粋 )

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

■ SampleServlet

package com.example;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.springframework.web.context.support.WebApplicationContextUtils;

@WebServlet("/Sample")
public class SampleServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Autowired
	private Service service;

	public SampleServlet() {
	}

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		
		PrintWriter out = response.getWriter();
		out.println("<html><body>");
		out.println(service.test());
		out.println("</body></html>");
	}
}

※ Service はサービスクラスのインターフェイス。実装したクラスに ServiceImpl があって @Component を付けている。

■ applicationContext.xml ( 抜粋 )

  <context:annotation-config />
  <context:component-scan base-package="com.example" />

しかし、上記でサーブレット実行すると見事にインジェクションされずに、service.test() で NullPointerException が発生しちゃいます。

調べてみると、以下の情報がありました。

・spring - Autowiring in servlet - Stack Overflow
http://stackoverflow.com/questions/11843690/autowiring-in-servlet

・Warlock's Thoughts: Spring - Using Beans in HTTP Servlet
http://vard-lokkur.blogspot.jp/2011/01/spring-using-beans-in-http-servlet.html

対応としては 2 つ紹介されていて、

■ 対応1

  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
  }

■ 対応2

  WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
  wac.getAutowireCapableBeanFactory().autowireBean(this);

があるみたいです。試した感じではどっちも無事インジェクションできる結果になりました。

・SpringBeanAutowiringSupport (Spring Framework 3.2.13.RELEASE API)
http://docs.spring.io/spring/docs/3.2.13.RELEASE/javadoc-api/org/springframework/web/context/support/SpringBeanAutowiringSupport.html

・AutowireCapableBeanFactory (Spring Framework 3.2.13.RELEASE API)
http://docs.spring.io/spring/docs/3.2.13.RELEASE/javadoc-api/org/springframework/beans/factory/config/AutowireCapableBeanFactory.html

しかし、なんで、上記の対応が必要なのか、上記の対応は何しているのかって点が正直よくわかりません・・・ ( サーブレットは DI コンテナの管理下にないからか?違うか・・・ )

理解というところはもっと勉強していずれできるようになるというところで、ひとまず今回は方法だけにしておきます。

以上です。

※ 備考
そもそもサーブレット内でこんなことするってのが、あんまり一般的ではないんかな。Spring MVC あんねんからそれ使おうよとか・・・

[ 環境情報 ]
Spring Framework 3.2.13.RELEASE
Java SE 8 Update 25
Apache Tomcat 8.0.21