Как добавить фильтры в сервлет без изменения файла web.xml - PullRequest
29 голосов
/ 25 августа 2011

Мне бы хотелось иметь возможность изменять / настраивать фильтры не так, как web.xml. Вот статическая конфигурация из 2 фильтров. Мне бы хотелось иметь возможность статически настраивать один фильтр и позволять этому фильтру загружать дополнительные фильтры. Я просто хотел узнать, знает ли кто-нибудь о lib, который уже имеет это.

Использование Servlet 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>

Я видел это в Guice с GuiceFilter, где фильтры настраиваются во время выполнения.

Ответы [ 3 ]

38 голосов
/ 29 сентября 2011

Просто сделайте ту же работу, что и контейнер. То есть заново изобрести колесо с шаблоном цепи ответственности , как это было под крышками, использованными фильтрами сервлетов.

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();
        }
    }

}

с этими маленькими вспомогательными классами (которые можно при необходимости сделать private static вложенными классами вышеупомянутых GodFilter):

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-файл конфигурации со всеми возможными фильтрами, чтобы вам было проще конфигурировать. Вы можете использовать отражение для создания фильтров в init() вашего GodFilter.

О, неважно, это то, что web.xml и контейнер уже делают ...

15 голосов
/ 25 августа 2011

Servlet 3.0 имеет аннотацию @WebFilter для определения фильтра.Больше не нужно объявлять это в web.xml.

Но загрузка фильтра из фильтра не поддерживается.Вы могли бы реализовать это сами: это «просто» схема цепочки ответственности, но зачем вам это?

4 голосов
/ 29 сентября 2011

Это может быть достигнуто простыми шагами, даже для спецификаций сервлетов до 3.0:

  1. Добавить фильтр, содержащий статическую и упорядоченную коллекцию классов (цепочка).
  2. Карта фильтра для перехвата каждого трафика.
  3. Управляйте порядком и существованием ваших вспомогательных классов (они будут вызываться вашим фильтром при перехвате трафика) в цепочке.

Ссылка: Xstream использует тот же тип шаблона для Serializer, но не с Servlet / Filter. :)

...