Spring拦截器Redis缓存失效求助(控制器@Cacheable正常)
问题分析与解决方案
你的拦截器中@Cacheable注解不生效的核心原因是Spring AOP的代理机制限制:当你在同一个类内部调用带有@Cacheable的方法时(比如preHandle直接调用authenticateUser),这个调用不会经过Spring的AOP代理,因此缓存逻辑不会被触发。而控制器中的@Cacheable能正常工作,是因为控制器方法是被外部的DispatcherServlet调用的,走了代理流程。
下面是两种可行的解决方案,优先推荐第一种:
方案1:将认证逻辑抽离到独立Service类(最佳实践)
把认证逻辑单独封装成一个Spring管理的Service组件,让拦截器依赖这个Service。这样调用Service的方法时,会经过AOP代理,缓存注解就能正常生效,同时也符合单一职责原则。
步骤1:创建AuthService
@Component public class AuthService { @Cacheable(value = "authenticateUser") public Map<String, List> authenticateUser(String username, String password) { // 原有的用户认证逻辑 return userRoles; } }
步骤2:修改拦截器注入并调用Service
@Component @Profile({"dev","prod"}) public class AuthenticationInterceptor extends HandlerInterceptorAdapter { private final AuthService authService; // 构造注入(推荐使用构造注入替代字段注入) @Autowired public AuthenticationInterceptor(AuthService authService) { this.authService = authService; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws UnauthorisedAccessException { // 改为调用AuthService的方法,触发缓存逻辑 Map<String, List> userRoles = authService.authenticateUser(username, password); // ... 后续权限校验逻辑 return authorizationResult; } }
方案2:获取自身代理对象调用方法(应急方案)
如果暂时不想抽离代码,可以让拦截器获取自己的Spring代理对象,通过代理对象调用authenticateUser方法,绕开内部调用的限制。不过这种方式不够优雅,不推荐长期使用。
@Component @Profile({"dev","prod"}) public class AuthenticationInterceptor extends HandlerInterceptorAdapter implements ApplicationContextAware { private AuthenticationInterceptor proxySelf; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 从Spring上下文获取当前类的代理对象 this.proxySelf = applicationContext.getBean(AuthenticationInterceptor.class); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws UnauthorisedAccessException { // 调用代理对象的方法,触发缓存 Map<String, List> userRoles = proxySelf.authenticateUser(username, password); // ... 后续逻辑 return authorizationResult; } @Cacheable(value = "authenticateUser") public Map<String, List> authenticateUser(String username, String password) { // 原有的认证逻辑 return userRoles; } }
额外检查项
确保你的项目满足以下条件:
- 已经在某个
@Configuration配置类上添加了@EnableCaching注解,且该配置类被Spring扫描到; - 拦截器是通过Spring容器注册的,而非直接new实例。比如在WebMvc配置类中注入拦截器:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { private final AuthenticationInterceptor authInterceptor; @Autowired public WebMvcConfig(AuthenticationInterceptor authInterceptor) { this.authInterceptor = authInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/**"); // 根据你的业务配置拦截路径 } }
内容的提问来源于stack exchange,提问作者rafrul
相关产品推荐
相关产品推荐

