一、怎么玩
1、引入pom.xml
org.springframework.boot
spring-boot-starter-web
2、自定义拦截器
//方式一
public class InterceptorM1 implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
logger.info("{}:在请求处理之前进行调用(Controller方法调用之前)", this.getClass().getSimpleName());
return true;//只有返回true才会继续向下执行,返回false取消当前请求
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
logger.info("{}:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)", this.getClass().getSimpleName());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
logger.info("{}:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)", this.getClass().getSimpleName());
}
}
//方式二
public class InterceptorM2 extends HandlerInterceptorAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
logger.info("{}:在请求处理之前进行调用(Controller方法调用之前)", this.getClass().getSimpleName());
return true;//只有返回true才会继续向下执行,返回false取消当前请求
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
logger.info("{}:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)", this.getClass().getSimpleName());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
logger.info("{}:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)", this.getClass().getSimpleName());
}
}
3、JavaConfig形式的配置
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new InterceptorM1()).addPathPatterns("/**");//用于添加拦截规则
registry.addInterceptor(new InterceptorM2()).addPathPatterns("/**");
// 多个拦截器组成一个拦截器链
// excludePathPatterns 用户排除拦截
}
}
4、Controller模拟
@RestController
public class Rest {
@GetMapping("/m1/{id}")
public String m1(@PathVariable("id") String id) {
return id;
}
@GetMapping("/m2/{id}")
public String m2(@PathVariable("id") String id) {
return id;
}
}
5、项目启动
二、嘛意思
1、HandlerInterceptorAdapter 和 HandlerInterceptor 嘛关系
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns {@code true}.这个实现总是返回true
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return 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 {
}
}
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
}
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;
}
总结:HandlerInterceptorAdapter实现了HandlerInterceptor接口得子接口,那我们在开发时是实现接口HandlerInterceptor呢还是继承类HandlerInterceptorAdapter,当让了有的时候我们只是实现一个方法,那我们就继承类HandlerInterceptorAdapter,如果全部实现实现接口HandlerInterceptor
2、拦截执行顺序
a、正常流程(拦截器拦截到任何都成功)
InterceptorM2--preHandle:在请求处理之前进行调用(Controller方法调用之前)
Controller
InterceptorM2--postHandle:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
InterceptorM1--postHandle:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
InterceptorM2--afterCompletion:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
InterceptorM1--afterCompletion:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
b、中断流程(M2失败了)
InterceptorM1--preHandle:在请求处理之前进行调用(Controller方法调用之前)
InterceptorM2--preHandle:在请求处理之前进行调用(Controller方法调用之前)
InterceptorM1--afterCompletion:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
如果有3个拦截器第2个中断,第三个就不执行了。
3、源码学习
public class HandlerExecutionChain {
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
// interceptorIndex默认是-1,如果n 个拦截器失败了,就n-1执行triggerAfterCompletion(request, response, null);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
/**
* Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
*/
void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
if (interceptors[i] instanceof AsyncHandlerInterceptor) {
try {
AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
}
catch (Throwable ex) {
logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
}
}
}
}
}
}
三、干嘛用
1、性能监控 (kaitao)
/**
* 性能监控
*/
public class PermHandlerInterceptor extends HandlerInterceptorAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
//Spring提供的一个命名的ThreadLocal实现
private NamedThreadLocal currentTimeThreadLocal = new NamedThreadLocal<>("startTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
PermIgnore annotation;
if (handler instanceof HandlerMethod){
annotation = ((HandlerMethod) handler).getMethodAnnotation(PermIgnore.class);
}else {
return true;
}
//如果有PermIgnore注解,则不性能检测
if (annotation != null) {
return true;
}
long startTime = System.currentTimeMillis();
currentTimeThreadLocal.set(startTime);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
PermIgnore annotation;
if (handler instanceof HandlerMethod){
annotation = ((HandlerMethod) handler).getMethodAnnotation(PermIgnore.class);
}else {
return;
}
//如果有PermIgnore注解,则不性能检测
if (annotation != null) {
return;
}
long endTime = System.currentTimeMillis();
long startTime = currentTimeThreadLocal.get();
long useTime = endTime - startTime;
if (useTime > 1) {
logger.info("[{}] use {} ms]", request.getRequestURI(), useTime);
}
}
}
/**
* 忽略性能监控,默认有
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermIgnore {
}
@RestController
public class Rest {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
@GetMapping("/api/{id}")
@PermIgnore
public String m1(@PathVariable("id") String id) {
logger.info("{}", this.getClass().getSimpleName());
return id;
}
@GetMapping("/api2/{id}")
public String m2(@PathVariable("id") String id) {
logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
return id;
}
}
m2
[/api2/2] use 53 ms]
Rest
2、登录检测(renren-fastplus)
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private SysUserTokenService tokenService;
public static final String USER_KEY = "userId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
AuthIgnore annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthIgnore.class);
} else {
return true;
}
//如果有@IgnoreAuth注解,则不验证token
if (annotation != null) {
return true;
}
//从header中获取token
String token = request.getHeader("token");
//如果header中不存在token,则从参数中获取token
if (StringUtils.isBlank(token)) {
token = request.getParameter("token");
}
//token为空
if (StringUtils.isBlank(token)) {
throw new RRException("token不能为空");
}
//查询token信息
SysUserTokenEntity tokenEntity = tokenService.queryByToken(token);
if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
throw new RRException("token失效,请重新登录", 520);
}
//设置userId到request里,后续根据userId,获取用户信息
request.setAttribute(USER_KEY, tokenEntity.getUserId());
return true;
}
}