最近在研究使用Redis+拦截器实现限流,大致实现方法为在拦截器中用RedisTemplate向Redis中记录请求次数,但是在测试中发现在拦截器中,redisTemplate注入不进来,一直为null。
我的拦截器如下:
public class AccessLimitInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...
int limit = 4;
int sec = 10;
String key = "111";
redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);
...
return true;
}
}
在自定义webConfig中加入上面定义的拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AccessLimitInterceptor()).addPathPatterns("/accessLimit/*");
}
}
使用postman调用的时候。发现出现了空指针异常,debug发现,AccessLimitInterceptor中的Redistemplate为null。
静下来想一想,拦截器加载的时间点在springcontext之前,即在bean实例化之前,所以在拦截器中注入自然为null 。那么我们就让拦截器执行的时候实例化拦截器Bean,在拦截器配置类里面先实例化拦截器,然后再获取就能解决这个问题啦。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public AccessLimitInterceptor getSessionInterceptor() {
return new AccessLimitInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionInterceptor()).addPathPatterns("/accessLimit/*");
}
}
再次运行,一切OK。
回头总结一下,其实想清楚rootcause这个问题很好解决,关键就是拦截器和bean的加载顺序。