Spring FrameworkにおけるDI
Spring FrameworkでDIを実現するには、設定クラスでBeanを定義してDIコンテナにBeanを登録する必要があります。
DIコンテナに登録したBeanは、ApplicationContextインターフェースを経由して取得することができます。
やりたいこと
通常、サーブレットフィルタ(javax.servlet.Filter)はサーブレットや静的リソースへのアクセス前後に共通処理を行う際に使われますが、ApplicationContextインターフェースを経由しないため、DIコンテナに登録したBeanを取り出すことができません。
そこで、サーブレットフィルタでDIを実現すべく、実装方法を調査するのが本記事の目的となります。
前提
- Java:Java11
- Spring Framework:5.2.2
- Spring Security:5.2.1
実装
いきなり答え合わせですが、web.xmlにてDelegatingFilterProxyをサーブレットフィルタとして定義した上で、対象フィルタ(ここではSampleFilter)のBean名を<filter-name>
に指定すればOKです。
これでSampleFilterでDIが可能となり、@Autowiredや@Injectが使えるようになります。
・web.xml
<filter>
<filter-name>sampleFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>sampleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
・SampleFilter.java
@Component("sampleFilter")
public class SampleFilter extends GenericFilterBean {
// 略
}
解説
サーブレットフィルタをweb.xmlで定義する場合、通常だと下記のように<filter-class>
にフィルタのパッケージを直接指定します。
<filter>
<filter-name>sampleFilter</filter-name>
<filter-class>○○.○○.sampleFilter</filter-class>
</filter>
しかし、パッケージ直接指定の場合はサーブレットフィルタでDIを実現できない問題が生じるため、どうにかしてDIを実現する形に持っていきたいわけです。
そこで、この問題を打開すべく、パッケージ直接指定ではなく、DelegatingFilterProxyを指定する形式に変えます。
<filter>
<filter-name>sampleFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
DelegatingFilterProxyについて、Qiitaの記事では以下のように解説されています。
DelegatingFilterProxy は、自身の
に設定された名前を使って Spring コンテナから javax.servlet.Filter を実装した Bean を取得する。
そして、その Bean に処理を委譲するだけのサーブレットフィルタになっている。
つまり、DelegatingFilterProxyがweb.xmlとApplicationContextインターフェースの橋渡し役となり、<filter-name>
に設定したBeanに処理を移譲することで、サーブレットフィルタでDIを実現することを可能としているわけです。
所感
要件を満たすべく、調査含め丸二日程ハマりました。
ただ、サーブレットフィルタでDIする機会は今後もありそうなので、余裕ある時期に実装できて逆によかったのかもしれない。。
参考
Spring Security 使い方メモ 基礎・仕組み
https://qiita.com/opengl-8080/items/c105152c9ca48509bd0c