自定义拦截器与一次请求的执行顺序

一、自定义拦截器

需求:需要在拦截器中获取到用户信息放到ThreadLocal<Map<String, Object>>中,在后续的操作需要得到ThreadLocal中的用户信息,进行方法校验(比如:用户权限、用户类型等等),来进行方法的执行,最后在一次请求完成后进行ThreadLocal资源的移除。

1. 继承HandlerInterceptorAdapter类

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
    public HandlerInterceptorAdapter() {
    }
    
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }

    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    }
}

preHandle:方法调用前执行,返回TRUE时进行下一个拦截器。一般进行预处理,如获取用户信息(一般将信息存放在 ThreadLocal 本地线程变量)、参数校验、权限校验、编码处理等。
postHandle:在方法执行后调用。
afterCompletion:在整个请求处理完毕后进行回调,也就是说视图渲染完毕或者调用方已经拿到响应。一般用来移除资源。

ThreadLocal

只属于当前线程,对应用户请求的用户信息只属于当前用户请求。
从名字我们就可以看到ThreadLocal 叫做本地线程变量,意思是说,ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。
ThreadLocal的坑 使用要注意的地方:
ThreadLocalMap的问题
由于ThreadLocalMap的key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。

如何避免泄漏
既然Key是弱引用,那么我们要做的事,就是在调用ThreadLocal的get()、set()方法时完成后再调用remove方法,将Entry节点和Map的引用关系移除,这样整个Entry对象在GC Roots分析后就变成不可达了,下次GC的时候就可以被回收。
如果使用ThreadLocal的set方法之后,没有显示的调用remove方法,就有可能发生内存泄露,所以养成良好的编程习惯十分重要,使用完ThreadLocal之后,记得调用remove方法。

2. 添加拦截器到Spring配置中

使用@Configuration注解 + 继承WebMvcConfigurationSupport类重写addInterceptors方法:

	// 添加自定义的拦截器
    protected void addInterceptors(InterceptorRegistry registry) {
    	registry.addInterceptor(...);
    }

二、执行顺序

一次web请求

一次web请求:会按照 filter -> interceptor -> controllerAdvice -> aspect -> controller的顺序执行调用

当controller抛出异常:也会按照controller -> aspect -> controllerAdvice -> interceptor -> filter来依次抛出
在这里插入图片描述

关系说明

下面用一张图说一下过滤器、Servlet容器、拦截器、AOP、Controller之间的关系:
在这里插入图片描述

ControllerAdvice

@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。

对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:

1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。

3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。

从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。

异常处理代码示例:

@Slf4j
@ControllerAdvice
public class NormalExceptionHandler {
	// 可以声明类型异常,如自定义异常等
	@ExceptionHandler(value = Exception.class)
	public Result exceptionHandler(Exception e) {
		Result result = "接口返回异常!";
		return result;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值