解决SpringBoot项目Interceptor中Redis注入异常
问题描述
修改博客系统后台,发现了在SpringBoot项目添加Interceptor拦截器时,Redis注入存在报空指针异常
Interceptor 简介
- Interceptor是什么
拦截器(Interceptor)同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
-
Interceptor用途
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等
- 权限检查:如登录检测,进入处理器检测是否登录
- 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)
- 通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。
-
Interceptor用法
要使用Interceptor的话,需要先实现springboot提供的 HandlerInterceptor类 :
public class MyInterceptor implements HandlerInterceptor {
}
java
其中HandlerInterceptor这个类源码:
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
java
里面有这三个接口
-
preHandle:这个是请求来的时候,访问controller之前,会先调用这里,可以在这里获取请求数据,请求方式等等,当这个方法返回false时表示请求结束、返回true则会继续调用下去
-
postHandle:在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用
-
afterCompletion:方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。
例如我们实现preHandle:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String name = method.getName();
System.out.println(name);
if (name.equals("getUserList")) {
return false;
}
}
return true;
}
}
java
上面做了对请求方法名getUserList的拦截
根据所需实现完之后,还需要在MVCConfig里面添加自定义的Interceptor:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//api请求过滤
registry.addInterceptor(new MyInterceptor());
}
}
java
就可以实现拦截功能了
Interceptor使用Redis
使用Redis场景
想对请求进行拦截,防止频繁提交,考虑用Redis做请求缓存,检查是否已经请求过了
遇到问题
在把redis进行注入的时候,发现redis没有被初始化,一直是null
public class MyInterceptor implements HandlerInterceptor {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String name = method.getName();
System.out.println(name);
if (name.equals("getUserList")) {
return false;
}
}
return true;
}
}
java
问题原因
查阅资料发现,拦截器在SpringContext初始化之前就执行了,Bean初始化之前它就执行了,所以它肯定是无法获取SpringIOC容器中的内容的。
解决方法
让拦截器执行的时候实例化拦截器Bean,在拦截器配置类里面先实例化拦截器,然后再获取就能解决这个问题啦
- 方法一:使用注解@Autowired
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//api请求过滤
registry.addInterceptor(myInterceptor);
}
}
java
- 方法二:使用注解@Bean
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public MyInterceptor getMyInterceptor(){
return new MyInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//api请求过滤
registry.addInterceptor(getMyInterceptor());
}
}
java