前言
今天在使用Spring cache的Cacheable注解的過程中遇見了一個Cacheable注解失效的問題,檢查問題發生的原因是因為Spring的Cacheable注解是基於Spring AOP實現的,但是類內部方法互相調用時不會被Spring AOP攔截的,所以導致被調用方法的Cacheable注解失效,特此記錄。
問題復現
@Service
public class UserServiceImpl{
@Override
public User detail(Long id) {
// 校驗信息
if (id == null || id == 0) {
return ApiResult.instance().fail(UserResultEnum.USER_ID_NULL);
}
User user = this.selectById(id);
if (user == null) {
return ApiResult.instance().fail(UserResultEnum.USER_NULL);
}
return user;
}
@Override
@Cacheable(value = "user",condition = "#id != null", key = "'user'.concat(#id.toString())")
public User selectById(Serializable id){
return super.selectById(id);
}
}
上述代碼在使用this.selectById的時候Cacheable注解是無效的,解決辦法如下:
- 寫一個工具類SpringContextUtil實現ApplicationContextAware接口
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext){
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
public static Object getBean(Class var1) throws BeansException {
return applicationContext.getBean(var1);
}
}
- 在Spring Boot啟動的時候將ApplicationContext注入SpringContextUtil
public class AuthServiceApplication {
public static void main(String[] args) {
SpringContextUtil springContextUtil = new SpringContextUtil();
ApplicationContext applicationContext = SpringApplication.run(AuthServiceApplication.class, args);
springContextUtil.setApplicationContext(applicationContext);
}
}
- 在UserServiceImpl方法中使用
@Service
public class UserServiceImpl{
@Override
public User detail(Long id) {
// 校驗信息
if (id == null || id == 0) {
return ApiResult.instance().fail(UserResultEnum.USER_ID_NULL);
}
//注入當前Bean使得調用內部方法也被SpringAOP攔截
IUserService userService = (IUserService) SpringContextUtil.getBean(this.getClass());
User user = userService.selectById(id);
if (user == null) {
return ApiResult.instance().fail(UserResultEnum.USER_NULL);
}
return user;
}
@Override
@Cacheable(value = "user",condition = "#id != null", key = "'user'.concat(#id.toString())")
public User selectById(Serializable id){
return super.selectById(id);
}
}
這樣就可以解決Bean內部方法調用不被Spring AOP攔截的問題