HandlerInterceptor в Spring

27 марта 2006

Иногда нужно чтобы на каждый запрос происходила одна и также определенная операция, как например выдернуть количество записей в базе для размещения на странице, или проверка прав доступа, или еще куча вариантов.
Я видел приложение написанное на Spring, в котором для всех контроллеров был один базовый класс, который и выполнял такую функциональность. Но на самом деле в Spring для этого есть такая вещь как HandlerInterceptor.

Bean, имплементирующий этот интерфейс можно указать в маппинге урлов и он будет вызыватся для каждого запроса. Лучше даже не имплементировать интерфейс HandlerInterceptor, а наследовать класс HandlerInterceptorAdapter, т.к. он имеет заглушки для всех методов, и достаточно переопределить лишь нужный метод. А методы такие:

boolean preHandle (request, response, handler)
Выполняется перед выполнением контроллера. Объект handler и есть контроллер который запустится следующим. Возвращает true в случае если можно продолжать дальше, или false, если вся работа уже сделана(например выдали ошибку о недостатке прав).
postHandle (request, response, handler, modelAndView)
Если нужно чтото сделать после выполнения контроллера, но до выдачи View (т.е. страница для выдачи еще не сгенерировалась). Имеет смысл если контроллер у нас выдал например ошибку или редирект, в таком случае, например, уже и не надо выдергивать количество объектов в базе. Или можно чтото переопределить после контроллера.
afterCompletion (request, response, handler, exception)
Вызывается уже после всего выполнения и выдачи страницы пользователю. Вызовется только в случае если сам контроллер отработал, если наш метод preHandle до этого выдал false, а значит и не вызывался контроллер, то и данный метод не вызовется. Если произошла ошибка, то она будет в атрибуте exception. Метод полезен если после выполнения нужно освободить какието ресурсы.

Для примера расширю пример с калькулятором, добавлю счетчик открытий страниц:

public class Counter extends HandlerInterceptorAdapter {

    private int start = 0;
    private int step = 1;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        start += step;
        request.setAttribute("count", start);
        return true;
    }

    public void setStart(int start) {
        this.start = start;
    }
    public void setStep(int step) {
        this.step = step;
    }
}

в наш spring.xml добавим следующее:

<bean id="Counter" class="example.simplespring.ctrl.Counter" >
    </bean>

и расширим urlMapping:

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="Counter"/>
            </list>
        </property>
        <property name="mappings">
            <props>
                <prop key="/calc.html">Calc</prop>
            </props>
        </property>
    </bean>

Все. Теперь если мы гдето на странице добавим

Counter: ${count}

то в этом месте будет выводится количество открытий страницы.

В общем суть заключается в том что когда нам постоянно нужно чтото подготавливать для выдачи пользователю, то мы делаем свои HandlerInterceptor'ы для каждого случая и добавляем их в список interceptors маппинга адресов.

16 Комментариев на “HandlerInterceptor в Spring”

  1. nroff сказал:

    Можешь помочь с проблемой: есть форма

    Цель визита: "/>

    Так вот в объект command записываются вместо русских букв — каракули, как и где надо прописать локализацию?

    Зараннее спасибо!

  2. igor сказал:

    Не особо понятно что же было в оригинале :)

    В общем я так понимаю что вам нужно настроить AS, если это Tomcat то прописать URIEncoding

  3. nroff сказал:

    Обрезался xml с формой и конфигом. Используется oc4j. В контроллере я могу задать кодировку для request'а и считать параметры в нужной кодировке. Но как быть с command object, который заполняется из формы автоматически? Можно как-нибудь задать кодировку для приложения в web.xml?

  4. igor сказал:

    C OC4J не знаю, пользовал его когда он был еще Orion AS, но это было давно и не помню.

    Но если у него нет подобной настройки, то может поможет использование CharacterEncodingFilter из Spring.

  5. nroff сказал:

    А как этот CharacterEncodingFilter прикрутить?

  6. igor сказал:

    Его как обычный фильтр, в web.xml. И указать кодировку в проперти encoding

  7. lucker сказал:

    Было бы не плохо еще добавить синхронизацию при изменении переменной start и сохранении ее в атрибут запроса.

  8. igor сказал:

    Лень синхронизацию писать для такого простого примера :)

    А значение сохраняется в атрибут.

  9. lucker сказал:

    ну только что лень и в демонстративных целях, а вообще, конечно, в реальных приложениях лучше не лениться.

  10. igor сказал:

    Оно на самом деле все эти i++ я и так всегда забываю синхронизировать, ну очень уж оно атомарным со стороны кажется :(

    Хотя чтото и не помню чтобы за последне время где то такое попадалось.

  11. lucker сказал:

    Кстати, насчет атомарно, есть такой пакет concurrent-atomic

    Очень удобно вот в таких вот ситуациях.

    AtomicInteger start = new AtomicInteger(0); .... request.setAttribute("count", start.incrementAndGet());

  12. Denis сказал:

    Имхо, достаточно сделать переменную start как volatile, и тогда синхронизация не нужна.

    Денис

  13. HitOdesit сказал:

    Не совсем понятно, зачем в примере класса Counter нужны сеттеры для start & step...?

    Просто для красоты?

  14. igor сказал:

    И вправду непонятно :) Видимо хотел что то на их примере показать, но пропустил. Ну в общем в любом случае для красоты.

  15. Владислав сказал:

    Как быть если есть два и более HandlerInterceptor'a

    и некоторые вызываются для всех страниц,

    некоторые для, например, двух?

  16. splix сказал:

    Ну собственно так и быть. А в чем именно тут возникла проблема?

    Спринг позволяет повешать сколько угодно хэндлеров, а уж он пусть сам думает что ему делать для конкретного адреса.

Оставьте свое мнение

XHTML: Вы можете использовать следующие html теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> Если в комментарии пишете XML то вместо "<" используйте "&lt;"

Благодарности, комментарии не по теме и пр. спам ссылками удаляется, к тому же автоматически, можете не стараться.