一般情況下,對來自瀏覽器的請求的攔截,是利用Filter實現的
而在Spring中,基於Filter這種方式可以實現Bean預處理、后處理。 比如注入FilterRegistrationBean,然后在這個Bean上傳遞自己繼承Filter實現的自定義Filter進入即可。
而Spring MVC也有攔截器,不僅可實現Filter的所有功能,還可以更精確的控制攔截精度。
Spring MVC提供的org.springframework.web.servlet.handler.HandlerInterceptorAdapter這個適配器,繼承此類,可以非常方便的實現自己的攔截器。
它有三個方法:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception { return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception { } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception { }
preHandle在業務處理器處理請求之前被調用。預處理,可以進行編碼、安全控制等處理;
postHandle在業務處理器處理請求執行完成后,生成視圖之前執行。后處理(調用了Service並返回ModelAndView,但未進行頁面渲染),有機會修改ModelAndView;
afterCompletion在DispatcherServlet完全處理完請求后被調用,可用於清理資源等。返回處理(已經渲染了頁面),可以根據ex是否為null判斷是否發生了異常,進行日志記錄;
如果基於XML配置使用Spring MVC,可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping進行Url映射(相當於struts的path映射)和攔截請求(注入interceptors)。
如果基於注解使用Spring MVC,可以使用DefaultAnnotationHandlerMapping注入interceptors。
注意無論基於XML還是基於注解,HandlerMapping Bean都是需要在XML中配置的。
示例一:
在這個例子中,我們假設UserController中的注冊操作只在9:00-12:00開放,那么就可以使用攔截器實現這個功能。
public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { private int openingTime; private int closingTime; private String mappingURL;//利用正則映射到需要攔截的路徑 public void setOpeningTime(int openingTime) { this.openingTime = openingTime; } public void setClosingTime(int closingTime) { this.closingTime = closingTime; } public void setMappingURL(String mappingURL) { this.mappingURL = mappingURL; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String url=request.getRequestURL().toString(); if(mappingURL==null || url.matches(mappingURL)){ Calendar c=Calendar.getInstance(); c.setTime(new Date()); int now=c.get(Calendar.HOUR_OF_DAY); if(now<openingTime || now>closingTime){ request.setAttribute("msg", "注冊開放時間:9:00-12:00"); request.getRequestDispatcher("/msg.jsp").forward(request, response); return false; } return true; } return true; } }
XML配置:
<bean id="timeBasedAccessInterceptor" class="com.spring.handler.TimeBasedAccessInterceptor"> <property name="openingTime" value="9" /> <property name="closingTime" value="12" /> <property name="mappingURL" value=".*/user\.do\?action=reg.*" /> </bean> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="timeBasedAccessInterceptor"/> </list> </property> </bean>
這里我們定義了一個mappingURL屬性,實現利用正則表達式對url進行匹配,從而更細粒度的進行攔截。當然如果不定義mappingURL,則默認攔截所有對Controller的請求。
UserController:
@Controller @RequestMapping("/user.do") public class UserController{ @Autowired private UserService userService; @RequestMapping(params="action=reg") public ModelAndView reg(Users user) throws Exception { userService.addUser(user); return new ModelAndView("profile","user",user); } // other option ... }
也可以配置多個攔截器,每個攔截器進行不同的分工。
示例二:
主要是XML配置不一樣
<!--配置攔截器, 多個攔截器,順序執行 --> <mvc:interceptors> <mvc:interceptor> <!-- 匹配的是url路徑, 如果不配置或/**,將攔截所有的Controller --> <mvc:mapping path="/" /> <mvc:mapping path="/user/**" /> <mvc:mapping path="/test/**" /> <bean class="com.alibaba.interceptor.CommonInterceptor"></bean> </mvc:interceptor> <!-- 當設置多個攔截器時,先按順序調用preHandle方法,然后逆序調用每個攔截器的postHandle和afterCompletion方法 --> </mvc:interceptors>
package com.alibaba.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.alibaba.util.RequestUtil; public class CommonInterceptor extends HandlerInterceptorAdapter{ private final Logger log = LoggerFactory.getLogger(CommonInterceptor.class); public static final String LAST_PAGE = "com.alibaba.lastPage"; /* * 利用正則映射到需要攔截的路徑 private String mappingURL; public void setMappingURL(String mappingURL) { this.mappingURL = mappingURL; } */ /** * 在業務處理器處理請求之前被調用 * 如果返回false * 從當前的攔截器往回執行所有攔截器的afterCompletion(),再退出攔截器鏈 * 如果返回true * 執行下一個攔截器,直到所有的攔截器都執行完畢 * 再執行被攔截的Controller * 然后進入攔截器鏈, * 從最后一個攔截器往回執行所有的postHandle() * 接着再從最后一個攔截器往回執行所有的afterCompletion() */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("GET".equalsIgnoreCase(request.getMethod())) { RequestUtil.saveRequest(); } log.info("==============執行順序: 1、preHandle================"); String requestUri = request.getRequestURI(); String contextPath = request.getContextPath(); String url = requestUri.substring(contextPath.length()); log.info("requestUri:"+requestUri); log.info("contextPath:"+contextPath); log.info("url:"+url); String username = (String)request.getSession().getAttribute("user"); if(username == null){ log.info("Interceptor:跳轉到login頁面!"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; }else return true; } /** * 在業務處理器處理請求執行完成后,生成視圖之前執行的動作 * 可在modelAndView中加入數據,比如當前時間 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("==============執行順序: 2、postHandle================"); if(modelAndView != null){ //加入當前時間 modelAndView.addObject("var", "測試postHandle"); } } /** * 在DispatcherServlet完全處理完請求后被調用,可用於清理資源等 * * 當有攔截器拋出異常時,會從當前攔截器往回執行所有的攔截器的afterCompletion() */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("==============執行順序: 3、afterCompletion================"); } }
參考:
http://blog.csdn.net/liuwenbo0920/article/details/7283757(以上內容部分轉自此篇文章)
http://www.cnblogs.com/xingele0917/p/4318008.html
http://blog.csdn.net/ye_sheng/article/details/48395663 (以上內容部分轉自此篇文章)