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 маппинга адресов.

апреля 4, 2006 в 02:44
Можешь помочь с проблемой: есть форма
Цель визита: "/>
Так вот в объект command записываются вместо русских букв — каракули, как и где надо прописать локализацию?
Зараннее спасибо!
апреля 4, 2006 в 06:20
Не особо понятно что же было в оригинале
В общем я так понимаю что вам нужно настроить AS, если это Tomcat то прописать URIEncoding
апреля 4, 2006 в 09:53
Обрезался xml с формой и конфигом. Используется oc4j. В контроллере я могу задать кодировку для request'а и считать параметры в нужной кодировке. Но как быть с command object, который заполняется из формы автоматически? Можно как-нибудь задать кодировку для приложения в web.xml?
апреля 4, 2006 в 10:01
C OC4J не знаю, пользовал его когда он был еще Orion AS, но это было давно и не помню.
Но если у него нет подобной настройки, то может поможет использование CharacterEncodingFilter из Spring.
апреля 5, 2006 в 01:21
А как этот CharacterEncodingFilter прикрутить?
апреля 6, 2006 в 07:21
Его как обычный фильтр, в web.xml. И указать кодировку в проперти encoding
апреля 21, 2006 в 09:21
Было бы не плохо еще добавить синхронизацию при изменении переменной start и сохранении ее в атрибут запроса.
апреля 21, 2006 в 09:49
Лень синхронизацию писать для такого простого примера
А значение сохраняется в атрибут.
апреля 21, 2006 в 10:02
ну только что лень и в демонстративных целях, а вообще, конечно, в реальных приложениях лучше не лениться.
апреля 21, 2006 в 10:06
Оно на самом деле все эти i++ я и так всегда забываю синхронизировать, ну очень уж оно атомарным со стороны кажется
Хотя чтото и не помню чтобы за последне время где то такое попадалось.
апреля 21, 2006 в 10:12
Кстати, насчет атомарно, есть такой пакет concurrent-atomic
Очень удобно вот в таких вот ситуациях.
AtomicInteger start = new AtomicInteger(0); .... request.setAttribute("count", start.incrementAndGet());июня 21, 2006 в 18:36
Имхо, достаточно сделать переменную start как volatile, и тогда синхронизация не нужна.
Денис
мая 15, 2007 в 12:01
Не совсем понятно, зачем в примере класса Counter нужны сеттеры для start & step...?
Просто для красоты?
мая 15, 2007 в 15:11
И вправду непонятно
Видимо хотел что то на их примере показать, но пропустил. Ну в общем в любом случае для красоты.
февраля 16, 2009 в 09:38
Как быть если есть два и более HandlerInterceptor'a
и некоторые вызываются для всех страниц,
некоторые для, например, двух?
февраля 17, 2009 в 14:54
Ну собственно так и быть. А в чем именно тут возникла проблема?
Спринг позволяет повешать сколько угодно хэндлеров, а уж он пусть сам думает что ему делать для конкретного адреса.