Как да добавите филтри към сървлета, без да променяте web.xml

Бих искал възможността да променям/конфигурирам филтри по начин, различен от web.xml. Ето статична конфигурация от 2 филтъра. Бих искал възможността да имам един филтър статично конфигуриран и да позволя на този филтър да зарежда допълнителни филтри. Просто исках да знам дали някой знае за 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>

Виждал съм това да се прави в Guice с GuiceFilter, където филтрите са конфигурирани по време на изпълнение.


person TJR    schedule 25.08.2011    source източник
comment
Това ще зависи от контейнера на сервлета, така че трябва да ни кажете кой от тях използвате   -  person SJuan76    schedule 25.08.2011
comment
Трябва ли да е зависимо? GuiceFilter зависи ли от контейнера?   -  person TJR    schedule 25.08.2011
comment
Guice пуска собствен механизъм за съпоставяне, който се държи точно като web.xml съпоставяния -- за уеб контейнера всички заявки завършват на GuiceFilter. Ако искате Guice, просто го използвайте :)   -  person Philipp Reichart    schedule 27.09.2011


Отговори (4)


Просто извършете същата работа, която вече върши контейнерът. т.е. преоткрийте колелото на модела за проектиране на веригата на отговорност, както е използван под завивките чрез сервлет филтри.

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 и контейнерът вече правят...

person BalusC    schedule 29.09.2011
comment
Не трябва ли GodFilterChain.doFilter() да премине през всички добавени филтри, отколкото да използва само първия? - person MRalwasser; 13.08.2012
comment
@MRalwasser: Ъ-ъ, не. Може би пропускате как работи шаблонът за проектиране на веригата на отговорност? Извикването chain.doFilter() в реализацията на филтъра би извикало отново GodFilterChain#doFilter() (защото е като this предадено като аргумент FilterChain) и след това итераторът ще премине към следващия елемент и т.н. - person BalusC; 13.08.2012
comment
ах, напълно пропуснах, че следващите извиквания на iterator.next() ще се извършват в по-дълбоките нива на веригата. Благодаря, че посочихте това. - person MRalwasser; 13.08.2012
comment
Работи чудесно! Но бихте ли обяснили използването на Pattern? Защо се нуждаем от замяна /* и задържане на позиция? - person Abylay Sabirgaliyev; 20.10.2014
comment
@user99560: така че един и същ код да не се налага ненужно да се изпълнява всеки път. Резултатът е един и същ всеки път, така че логично е по-ефективно да го изчислите веднъж и да го вземете за повторна употреба. - person BalusC; 22.05.2015
comment
това са причините да имате 629k. това е впечатляващо и чисто. +1 - person natedennis; 11.02.2016
comment
@BalusC, това може да е друг въпрос за публикуване, но има ли начин да конфигурирате филтри в различен xml файл, различен от дефинирането им в web.xml? - person Agent47; 21.03.2020

Servlet 3.0 има анотацията @WebFilter за дефиниране на филтър. Вече няма нужда да го декларирате в web.xml.

Но зареждането на филтър от филтър не се поддържа. Бихте могли да го приложите сами: това е „просто“ моделът на веригата от отговорност, но защо бихте го направили?

person JB Nizet    schedule 25.08.2011
comment
Изтри отговора ми, изпревари ме с 30 секунди ›:| - person Dave; 25.08.2011
comment
@JB Nizet : TJR не иска версия servlet 3.0, той е специфичен за servlet 2.5 - person developer; 25.08.2011
comment
@JB, добавих към въпроса, че използвам 2.5. Тази функция 3.0 обаче ме прави щастлив. - person TJR; 25.08.2011
comment
Какво търси @WebFilter(filterName = "customFilter", urlPatterns = { "/*" }) public class CustomFilter implements Filter .... го прави - person absin; 22.02.2018

Може да се постигне с лесни стъпки, дори за спецификация на Servlet преди 3.0:

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

Справка: Xstream използва същия вид модел за сериализатор, но не и със сервлет/филтър. :)

person Puspendu Banerjee    schedule 29.09.2011

Аз лично харесвам @WebFilter анотация за регистриране на сървлет филтри.
Но друго решение е да добавите филтъра по време на изпълнение с помощта на ServletContext's addFilter функция.

Изпълнение ServletContextListener; нещо като:

public class MyContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent ce) {
        ServletContext servletContext = ce.getServletContext();
    
        // you can even conditionally add this
        servletContext.addFilter("My filter 1", MyFilter1.class)
                .addMappingForUrlPatterns(allOf(DispatcherType.class), false, "/*");
    }
}

Регистрирайте слушател:

<listener>
    <listener-class>com.me.MyContextListener</listener-class>
</listener>   

И, разбира се, трябва да внедрите Филтър. Но във вашия въпрос вече се позовавате на примерен филтър „MyFilter1“.

person R. Oosterholt    schedule 14.07.2021