import org.springframework.web.servlet.HandlerInterceptor;
前言
拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是 AOP 的一种实现策略。Java 里的拦截器是动态拦截 action 调用的对象,其依赖的技术就是 Java 的动态代理。
同时也是提供了一种提取 action 中可重用的部分的方式。
拦截器只对 Action 起作用。大部分时候,拦截器方法都是通过代理的方式来调用的。
Spring自定义拦截器的步骤
- 自定义一个实现了Interceptor接口的类,或者继承抽象类AbstractInterceptor。
- 第二步:在配置文件中注册定义的拦截器。
- 第三步:在需要使用Action中引用上述定义的拦截器,为了方便也可以将拦截器定义为默认的拦截器,这样在不加特殊说明的情况下,所有的Action都被这个拦截器拦截。
用Spring实现拦截器Interceptor的拦截功能
在Spring框架之中,我们要想实现拦截器的功能,主要通过两种途径,第一种是实现HandlerInterceptor
接口,第二种是实现WebRequestInterceptor
接口。
HandlerInterceptor
在实际应用中,我们一般都是通过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类,复写preHandle()
、postHandle()
和afterCompletion()
这 3 个方法来对用户的请求进行拦截处理的。
1.preHandle
preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)
preHandle方法都会在Controller方法调用之前调用
。可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理。
Spring MVC 中的Interceptor是链式调用的。
该方法的返回值是布尔(Boolean)类型的,当它返回为false
时,表示请求结束,后续的Interceptor和控制器(Controller)都不会再执行;当返回值为true
时,就会继续调用下一个Interceptor的preHandle
方法,如果已经是最后一个Interceptor的时候,就会是调用当前请求的控制器中的方法。
2.postHandle
postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)
在Controller的方法调用之后执行
只能在当前所属的Interceptor的preHandle
方法的返回值为true
的时候,才能被调用。它会在DispatcherServlet进行视图返回渲染之前被调用
,所以我们可以在这个方法中对控制器处理之后的ModelAndView对象进行操作。
先声明的Interceptor的postHandle方法会后执行。
3.afterCompletion
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
Interceptor的preHandle
方法的返回值为true时才会执行。
因此,该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行
.
这个方法的主要作用是用于进行资源清理的工作。
拦截器示例(登陆拦截)
public class LoginInterceptor extends HandlerInterceptorAdapter{
/**
* 在业务处理器处理请求之前被调用
* 如果返回false
* 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
* 如果返回true
* 执行下一个拦截器,直到所有的拦截器都执行完毕
* 再执行被拦截的Controller
* 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle()
* 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String username = (String)request.getSession().getAttribute("user");
if(username == null){
//跳转到login页面
request.getRequestDispatcher("/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 {
if(modelAndView != null){ //加入当前时间
modelAndView.addObject("name", "小明");
}
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
*
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
public class LoginInterceptor extends HandlerInterceptorAdapter{
/**
* 在业务处理器处理请求之前被调用
* 如果返回false
* 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
* 如果返回true
* 执行下一个拦截器,直到所有的拦截器都执行完毕
* 再执行被拦截的Controller
* 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle()
* 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String username = (String)request.getSession().getAttribute("user");
if(username == null){
//跳转到login页面
request.getRequestDispatcher("/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 {
if(modelAndView != null){ //加入当前时间
modelAndView.addObject("name", "小明");
}
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
*
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
配置拦截器(applicationContext.xml)
1.概念
-
Join Point
,表示“连接点”,它是程序运行中的某个阶段点,比如方法的调用、异常的抛出等; -
Advice
,表示“通知”,它是某个连接点所采用的处理逻辑,也就是向连接点注入的代码; -
Pointcut
,表示“切入点”,它是“连接点”的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发; -
Advisor
,它是Pointcut和Advice的配置器,包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码。
2.XML 配置文件的声明
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
3.mvc:interceptors标签配置拦截器
<mvc:interceptors>
<!-- 直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求。先声明的拦截器先执行,后声明的拦截器后执行 -->
<bean class="com.interceptor.AllInterceptor"/>
<bean class="com.interceptor.ContextInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/demo/hello.do"/>
<mvc:exclude-mapping path="/login"/>
<!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->
<bean class="com.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:interceptors>
<!-- 直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求。先声明的拦截器先执行,后声明的拦截器后执行 -->
<bean class="com.interceptor.AllInterceptor"/>
<bean class="com.interceptor.ContextInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/demo/hello.do"/>
<mvc:exclude-mapping path="/login"/>
<!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->
<bean class="com.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
4.aop切面配置拦截器
<bean id="WrongCodeInterceptor" class="com.interceptor.WrongCodeInterceptor">
<property name="userName" value="小明"></property>
</bean>
<bean id="loginInterceptor" class="com.interceptor.LoginInterceptor">
<property name="excludePackages">
<list>
<value>com.user</value>
<value>com.order</value>
</list>
</property>
</bean>
<aop:config>
<aop:advisor advice-ref="WrongCodeInterceptor" pointcut="execution(* com.*.demo..*.*(..)) " />
<aop:advisor advice-ref="loginInterceptor" pointcut="execution(* com.*.demo..*.*(..))" />
</aop:config>
<bean id="WrongCodeInterceptor" class="com.interceptor.WrongCodeInterceptor">
<property name="userName" value="小明"></property>
</bean>
<bean id="loginInterceptor" class="com.interceptor.LoginInterceptor">
<property name="excludePackages">
<list>
<value>com.user</value>
<value>com.order</value>
</list>
</property>
</bean>
<aop:config>
<aop:advisor advice-ref="WrongCodeInterceptor" pointcut="execution(* com.*.demo..*.*(..)) " />
<aop:advisor advice-ref="loginInterceptor" pointcut="execution(* com.*.demo..*.*(..))" />
</aop:config>
5.Spring Boot不需要在xml中配置,需要注册拦截器
编写配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public LoginInterceptor sessioninterceptor() {
return new LoginInterceptor();
}
/**
* 配置拦截器
*
* @param registry
* @author yuqingquan
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(sessioninterceptor()).addPathPatterns("/**")
.excludePathPatterns("/**/login")
.order(1); //指定执行顺序,数值越小越优先
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public LoginInterceptor sessioninterceptor() {
return new LoginInterceptor();
}
/**
* 配置拦截器
*
* @param registry
* @author yuqingquan
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(sessioninterceptor()).addPathPatterns("/**")
.excludePathPatterns("/**/login")
.order(1); //指定执行顺序,数值越小越优先
}
}
SpringMVC 拦截器原理