web-dev-qa-db-ja.com

web.xmlを変更せずにサーブレットにフィルターを追加する方法

Web.xmlとは異なる方法でフィルターを変更/構成する機能が欲しい。 2つのフィルターの静的構成を次に示します。 1つのフィルターを静的に構成し、そのフィルターが追加のフィルターをロードできるようにしたいのですが。既にこれを持っているlibを知っている人がいるかどうかを知りたかっただけです。

サーブレットAPI 2.5の使用

<web-app>
  ...
  <filter>
    <filter-name>MyFilter1</filter-name>
    <filter-class>com.me.MyFilter1</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>MyFilter1</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
  <filter>
    <filter-name>MyFilter2</filter-name>
    <filter-class>com.me.MyFilter2</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>MyFilter2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
</web-app>

実行時にフィルターが設定されるGuiceFilterを使用して、Guiceでこれを実行しました。

29
TJR

コンテナが既に行っているのと同じ仕事をするだけです。つまり 責任のチェーン 設計パターンの車輪を再発明します。これは、サーブレットフィルターで使用されているカバーの下にあります。

_public class GodFilter implements Filter {

    private Map<Pattern, Filter> filters = new LinkedHashMap<Pattern, Filter>();

    @Override
    public void init(FilterConfig config) throws ServletException {
        Filter1 filter1 = new Filter1();
        filter1.init(config);
        filters.put(new Pattern("/foo/*"), filter1);

        Filter2 filter2 = new Filter2();
        filter2.init(config);
        filters.put(new Pattern("*.bar"), filter2);

        // ...
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest hsr = (HttpServletRequest) request;
        String path = hsr.getRequestURI().substring(hsr.getContextPath().length());
        GodFilterChain godChain = new GodFilterChain(chain);

        for (Entry<Pattern, Filter> entry : filters.entrySet()) {
            if (entry.getKey().matches(path)) {
                godChain.addFilter(entry.getValue());
            }
        }

        godChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        for (Filter filter : filters.values()) {
            filter.destroy();
        }
    }

}
_

これらの小さなヘルパークラス(必要に応じて、上記のGodFilterのネストされたクラス_private static_を作成できます):

_public class Pattern {

    private int position;
    private String url;

    public Pattern(String url) {
        this.position = url.startsWith("*") ? 1
                      : url.endsWith("*") ? -1
                      : 0;
        this.url = url.replaceAll("/?\\*", "");
    }

    public boolean matches(String path) {
        return (position == -1) ? path.startsWith(url)
             : (position == 1) ? path.endsWith(url)
             : path.equals(url);
    }

}
_

そして

_public class GodFilterChain implements FilterChain {

    private FilterChain chain;
    private List<Filter> filters = new ArrayList<Filter>();
    private Iterator<Filter> iterator;

    public GodFilterChain(FilterChain chain) {
        this.chain = chain;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (iterator == null) {
            iterator = filters.iterator();
        }

        if (iterator.hasNext()) {
            iterator.next().doFilter(request, response, this);
        } else {
            chain.doFilter(request, response);
        }
    }

    public void addFilter(Filter filter) {
        if (iterator != null) {
            throw new IllegalStateException();
        }

        filters.add(filter);
    }

}
_

必要に応じて、可能なすべてのフィルターをXML構成ファイルにフィードして、構成を簡単にすることもできます。リフレクションを使用して、GodFilterinit()にフィルターを作成できます。

ああ、気にしない、それは_web.xml_とコンテナがすでにしていることです...

38
BalusC

サーブレット3.0には@WebFilterフィルターを定義する注釈。 web.xmlで宣言する必要はもうありません。

ただし、フィルターからのフィルターのロードはサポートされていません。自分で実装することもできます。それは「単なる」責任の連鎖パターンですが、なぜそうするのでしょうか?

15
JB Nizet

3.0より前のサーブレット仕様でも、簡単な手順で実現できます。

  1. クラスの静的で順序付けられたコレクション(チェーン)を含むフィルターを追加します。
  2. すべてのトラフィックをインターセプトするためにフィルターをマップします。
  3. チェーン内のヘルパークラス(トラフィックのインターセプト時にフィルターによってプライベートに呼び出されます)の順序と存在を操作します。

参照:Xstreamはシリアライザーにも同じ種類のパターンを使用しますが、サーブレット/フィルターでは使用しません。 :)

4