处理器拦截器
SpringMVC的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
1)常见应用场景
1、日志记录
2、权限检查
3、性能监控
4、通用行为 例如读取用户cookie
5、OpenSessionInView 例如在Hibernate中,在进入处理器前打开Session,在完成后关闭Session。
等
2)拦截器接口
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}
preHandle方法
预处理回调方法,实现处理器的预处理,第三个参数为的处理器(本次请求要访问的那个Controller)
返回值:true表示继续流程(如调用下一个拦截器或处理器)
false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
postHandle方法
后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
afterCompletion方法
整个请求处理完毕回调方法,即在视图渲染完毕时回调
3)拦截器适配器
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现,不管你需不需要,此时spring 提供了一个HandlerInterceptorAdapter 适配器(适配器模式),允许我们只实现需要的回调方法。
在HandlerInterceptorAdapter中,对HandlerInterceptor 接口中的三个方法都进行了空实现,其中preHandle方法的返回值,默认是true
4)测试一个拦截器
拦截器代码:
public class MyInterceptor1 extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor1 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1 postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor1 afterCompletion");
}
}
配置文件:
<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
</list>
</property>
</bean>
访问一个测试的Controller查看结果:
MyInterceptor1 preHandle
TestController执行
MyInterceptor1 postHandle
MyInterceptor1 afterCompletion
5)测试俩个拦截器
俩个拦截器的代码和上面类似,只是每个输出的内容不同
配置文件:
<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
访问一个测试的Controller查看结果:
MyInterceptor1 preHandle
MyInterceptor2 preHandle
TestController执行
MyInterceptor2 postHandle
MyInterceptor1 postHandle
MyInterceptor2 afterCompletion
MyInterceptor1 afterCompletion
注意:<list>标签中引用拦截器的顺序会影响结果输出的顺序
6)如果Controller等采用的注解配置,那么拦截器需要mvc标签进行配置
注意:每个<mvc:interceptor>只能配置一个拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="handlerInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
例如1: 注意/*和/**的区别
<mvc:interceptors>
<!-- 下面所有的mvc映射路径都会被这个拦截器拦截 -->
<bean class="com.briup.web.interceptor.MyInterceptor1" />
<mvc:interceptor>
<mapping path="/**"/>
<exclude-mapping path="/admin/**"/>
<bean class="com.briup.web.interceptor.MyInterceptor2" />
</mvc:interceptor>
<mvc:interceptor>
<mapping path="/secure/*"/>
<bean class="com.briup.web.interceptor.MyInterceptor3" />
</mvc:interceptor>
</mvc:interceptors>
7)拦截器是单例
因此不管多少用户请求多少次都只有一个拦截器实现,即线程不安全。
所以在必要时可以在拦截器中使用ThreadLocal,它是和线程绑定,一个线程一个ThreadLocal,A 线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal。
8)记录执行Controller所用时间
public class TimeInterceptor extends HandlerInterceptorAdapter{
//拦截器是单例,不是线程安全的,所以这里使用ThreadLocal
private ThreadLocal<Long> local = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long start = System.currentTimeMillis();
local.set(start);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
long end = System.currentTimeMillis();
System.out.println("共耗时:"+(end-local.get()));
}
}
9)登录检查
public class LoginInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//请求到登录页面放行
if(request.getServletPath().startsWith("/login")) {
return true;
}
//如果用户已经登录放行
if(request.getSession().getAttribute("username") != null) {
return true;
}
//重定向到登录页面
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
}
SpringMVC的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
1)常见应用场景
1、日志记录
2、权限检查
3、性能监控
4、通用行为 例如读取用户cookie
5、OpenSessionInView 例如在Hibernate中,在进入处理器前打开Session,在完成后关闭Session。
等
2)拦截器接口
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}
preHandle方法
预处理回调方法,实现处理器的预处理,第三个参数为的处理器(本次请求要访问的那个Controller)
返回值:true表示继续流程(如调用下一个拦截器或处理器)
false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
postHandle方法
后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
afterCompletion方法
整个请求处理完毕回调方法,即在视图渲染完毕时回调
3)拦截器适配器
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现,不管你需不需要,此时spring 提供了一个HandlerInterceptorAdapter 适配器(适配器模式),允许我们只实现需要的回调方法。
在HandlerInterceptorAdapter中,对HandlerInterceptor 接口中的三个方法都进行了空实现,其中preHandle方法的返回值,默认是true
4)测试一个拦截器
拦截器代码:
public class MyInterceptor1 extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor1 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1 postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor1 afterCompletion");
}
}
配置文件:
<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
</list>
</property>
</bean>
访问一个测试的Controller查看结果:
MyInterceptor1 preHandle
TestController执行
MyInterceptor1 postHandle
MyInterceptor1 afterCompletion
5)测试俩个拦截器
俩个拦截器的代码和上面类似,只是每个输出的内容不同
配置文件:
<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
访问一个测试的Controller查看结果:
MyInterceptor1 preHandle
MyInterceptor2 preHandle
TestController执行
MyInterceptor2 postHandle
MyInterceptor1 postHandle
MyInterceptor2 afterCompletion
MyInterceptor1 afterCompletion
注意:<list>标签中引用拦截器的顺序会影响结果输出的顺序
6)如果Controller等采用的注解配置,那么拦截器需要mvc标签进行配置
注意:每个<mvc:interceptor>只能配置一个拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="handlerInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
例如1: 注意/*和/**的区别
<mvc:interceptors>
<!-- 下面所有的mvc映射路径都会被这个拦截器拦截 -->
<bean class="com.briup.web.interceptor.MyInterceptor1" />
<mvc:interceptor>
<mapping path="/**"/>
<exclude-mapping path="/admin/**"/>
<bean class="com.briup.web.interceptor.MyInterceptor2" />
</mvc:interceptor>
<mvc:interceptor>
<mapping path="/secure/*"/>
<bean class="com.briup.web.interceptor.MyInterceptor3" />
</mvc:interceptor>
</mvc:interceptors>
7)拦截器是单例
因此不管多少用户请求多少次都只有一个拦截器实现,即线程不安全。
所以在必要时可以在拦截器中使用ThreadLocal,它是和线程绑定,一个线程一个ThreadLocal,A 线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal。
8)记录执行Controller所用时间
public class TimeInterceptor extends HandlerInterceptorAdapter{
//拦截器是单例,不是线程安全的,所以这里使用ThreadLocal
private ThreadLocal<Long> local = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long start = System.currentTimeMillis();
local.set(start);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
long end = System.currentTimeMillis();
System.out.println("共耗时:"+(end-local.get()));
}
}
9)登录检查
public class LoginInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//请求到登录页面放行
if(request.getServletPath().startsWith("/login")) {
return true;
}
//如果用户已经登录放行
if(request.getSession().getAttribute("username") != null) {
return true;
}
//重定向到登录页面
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
}
注意:推荐能使用servlet规范中的过滤器Filter实现的功能就用Filter实现,因为HandlerInteceptor只有在SpringWebMVC环境下才能使用,因此Filter是最通用的、最先应该使用的。