环境
- SpringBoot 环境
相关
- spring-boot在web层面使用了spring mvc的拦截器功能,并没有做其他处理,故我们只要熟悉mvc的拦截器,自然而然可以将拦截器加入到spring-boot上
- 已知request 请求处理顺序,Filter -> HandlerInterceptor 拦截器 -> AOP
- Filter 过滤器属于 Servlet 范畴的API, 与Spring 没什么关系
- HandlerInterceptor 拦截器 属于Spring 的范畴
- 只有经过 dispatchservlet 的请求,才会走拦截器chain,我们自定义的的servlet 请求是不会被拦截的
- 但过滤器会拦截所有请求
拦截器提供更精细的控制-时间段区分
- 在request被controller 响应之前,只有返回true 才会进入后续interceptor 和 controller
- 在request被响应之后-dispatcherServlet进行视图渲染之前,可以在此处对Controller 处理后的ModelAndView对象进行处理
- 视图渲染之后,用于资源清理
特点
我们不能通过修改拦截器修改request内容,但可以通过抛出异常(或者返回false)暂停request执行
案例
基础
Spring MVC 中拦截器顶层是 HandlerInterceptor, 它有3个方法
public interface HandlerInterceptor {
//controller 方法执行前执行,当return TRUE 时进入下一个 拦截器 或直接进入 Controller 方法
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;
}
通常情况下继承 HandlerInterceptorAdapter 抽象类 来实现自定义拦截器
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns true
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true; //硬编码为TRUE,自定义拦截器 可以覆盖
}
/**
* This implementation is empty.
*/
@Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
//空实现
}
/**
* This implementation is empty.
*/
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
//空实现
}
/**
* This implementation is empty.
*/
@Override
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
AsyncHandlerInterceptor接口代码
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
/**
* Called instead of {@code postHandle} and {@code afterCompletion}, when
* the a handler is being executed concurrently.
* Implementations may use the provided request and response but should
* avoid modifying them in ways that would conflict with the concurrent
* execution of the handler. A typical use of this method would be to
* clean up thread-local variables.
* @param request the current request
* @param response the current response
* @param handler the handler (or {@link HandlerMethod}) that started async
* execution, for type and/or instance examination
* @throws Exception in case of errors
*/
void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
}
注
- HandlerInterceptorAdapter 是 适配器 模式的一种
- 实现适配器模式有两种方式:继承 和 组合
- 实现接口时,可以将某些接口方法写死,然后让继承类覆盖
- AsyncHandlerInterceptor 接口只增添了一个接口,这种设计模式可以留意下
自定义拦截器
@Slf4j
public class MyHandlerInterceptor1 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("handler interceptor1 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("handler interceptor1 postHandle");
//super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("handler interceptor1 afterComplete");
//super.afterCompletion(request, response, handler, ex);
}
}
@Slf4j
public class MyHandlerInterceptor2 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("handler interceptor2 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("handler interceptor2 postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("handler interceptor2 afterComplete");
}
}
注册拦截器
@Configuration //标记为配置类,本质上是将该类纳入上下文从而执行addInterceptor动作,故@Component注解亦可
public class MyInterceptorConfigure extends WebMvcConfigurerAdapter {
//继承WebMVCConfigureAdapter,并覆盖 addInterceptror 方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyHandlerInterceptor1());
registry.addInterceptor(new MyHandlerInterceptor2()); //添加顺序决定了拦截顺序
super.addInterceptors(registry);
}
}
controller
@RestController
@RequestMapping("/interceptor")
public class MyInterceptorController {
@RequestMapping("") //没有主method属性,表示接受任意method请求
public String testIntercept(String param) { //未加 @RequestParam() 同样也可绑定方法参数param
System.out.println("param: " + param);
return param;
}
}
运行结果
- 运行方式:启动server,postman 发送请求 url: http://localhost:8080/interceptor?param=test
2018-01-17 01:16:50.993 INFO 568 --- [ main] c.l.s.interceptor.MyHandlerInterceptor1 : handler interceptor1 preHandle
2018-01-17 01:16:50.993 INFO 568 --- [ main] c.l.s.interceptor.MyHandlerInterceptor2 : handler interceptor2 preHandle
param: test
2018-01-17 01:16:51.029 INFO 568 --- [ main] c.l.s.interceptor.MyHandlerInterceptor2 : handler interceptor2 postHandle
2018-01-17 01:16:51.030 INFO 568 --- [ main] c.l.s.interceptor.MyHandlerInterceptor1 : handler interceptor1 postHandle
2018-01-17 01:16:51.032 INFO 568 --- [ main] c.l.s.interceptor.MyHandlerInterceptor2 : handler interceptor2 afterComplete
2018-01-17 01:16:51.032 INFO 568 --- [ main] c.l.s.interceptor.MyHandlerInterceptor1 : handler interceptor1 afterComplete
添加Aspect后的运行结果
Aspect 代码
@Component
@Aspect
@Slf4j
public class TestAspect {
@Pointcut(("execution(public * com.lance.spring.controller.*.*(..))"))
public void restPoint() {
}
@Before("restPoint()")
public void before(JoinPoint joinPoint) {
log.info("aspect before pointcut");
}
@After("restPoint()")
public void after(JoinPoint joinPoint) {
log.info("aspect after pointcut");
}
@Around("restPoint()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("aspect around before target method");
joinPoint.proceed();
log.info("aspect around after target method");
}
}
结果
2018-01-17 01:19:42.860 INFO 6256 --- [ main] c.l.s.interceptor.MyHandlerInterceptor1 : handler interceptor1 preHandle
2018-01-17 01:19:42.860 INFO 6256 --- [ main] c.l.s.interceptor.MyHandlerInterceptor2 : handler interceptor2 preHandle
2018-01-17 01:19:42.889 INFO 6256 --- [ main] com.lance.spring.aop.TestAspect : aspect around before target method
2018-01-17 01:19:42.889 INFO 6256 --- [ main] com.lance.spring.aop.TestAspect : aspect before pointcut
param: test
2018-01-17 01:19:42.899 INFO 6256 --- [ main] com.lance.spring.aop.TestAspect : aspect around after target method
2018-01-17 01:19:42.899 INFO 6256 --- [ main] com.lance.spring.aop.TestAspect : aspect after pointcut
2018-01-17 01:19:42.915 INFO 6256 --- [ main] c.l.s.interceptor.MyHandlerInterceptor2 : handler interceptor2 postHandle
2018-01-17 01:19:42.916 INFO 6256 --- [ main] c.l.s.interceptor.MyHandlerInterceptor1 : handler interceptor1 postHandle
2018-01-17 01:19:42.916 INFO 6256 --- [ main] c.l.s.interceptor.MyHandlerInterceptor2 : handler interceptor2 afterComplete
2018-01-17 01:19:42.916 INFO 6256 --- [ main] c.l.s.interceptor.MyHandlerInterceptor1 : handler interceptor1 afterComplete