在应用程序启动时缓存查找不起作用
Caching lookups on application startup doesn't work
我在 Tomcat 9.0.2 上使用 Spring Boot 1.5.9,我正在尝试使用 spring @Cacheable[=27= 缓存查找] 安排在应用程序启动时运行并每 24 小时重复一次的缓存刷新作业,如下所示:
@Component
public class RefreshCacheJob {
private static final Logger logger = LoggerFactory.getLogger(RefreshCacheJob.class);
@Autowired
private CacheService cacheService;
@Scheduled(fixedRate = 3600000 * 24, initialDelay = 0)
public void refreshCache() {
try {
cacheService.refreshAllCaches();
} catch (Exception e) {
logger.error("Exception in RefreshCacheJob", e);
}
}
}
缓存服务如下:
@Service
public class CacheService {
private static final Logger logger = LoggerFactory.getLogger(CacheService.class);
@Autowired
private CouponTypeRepository couponTypeRepository;
@CacheEvict(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES, allEntries = true)
public void clearCouponsTypesCache() {}
public void refreshAllCaches() {
clearCouponsTypesCache();
List<CouponType> couponTypeList = couponTypeRepository.getCoupons();
logger.info("######### couponTypeList: " + couponTypeList.size());
}
}
存储库代码:
public interface CouponTypeRepository extends JpaRepository<CouponType, BigInteger> {
@Query("from CouponType where active=true and expiryDate > CURRENT_DATE order by priority")
@Cacheable(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES)
List<CouponType> getCoupons();
}
稍后在我的网络服务中,当尝试按如下方式进行查找时:
@GET
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@Path("/getCoupons")
@ApiOperation(value = "")
public ServiceResponse getCoupons(@HeaderParam("token") String token, @HeaderParam("lang") String lang) throws Exception {
try {
List<CouponType> couponsList = couponRepository.getCoupons();
logger.info("###### couponsList: " + couponsList.size());
return new ServiceResponse(ErrorCodeEnum.SUCCESS_CODE, resultList, errorCodeRepository, lang);
} catch (Exception e) {
logger.error("Exception in getCoupons webservice: ", e);
return new ServiceResponse(ErrorCodeEnum.SYSTEM_ERROR_CODE, errorCodeRepository, lang);
}
}
第一次调用它从数据库中获取查找,随后的调用从缓存中获取,而它应该在 Web 服务中的第一次调用中从缓存中获取?
为什么我会出现这种行为,我该如何解决?
@CacheEvict
在同一服务中调用时不会被调用。这是因为 Spring 围绕服务创建了一个代理,只有来自 "outside" 的调用通过缓存代理。
解决方法是添加
@CacheEvict(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES, allEntries = true)
到 refreshAllCaches
,或者将 refreshAllCaches
移动到调用 ICacheService.clearCouponsTypeCache
.
的新服务中
升级到 Tomcat 9.0.4
后问题已解决
虽然它本身不影响计划任务,但当在 CacheService
中调用 refreshAllCaches()
时,clearCouponsTypesCache()
上的 @CacheEvict
被绕过,因为它是从相同的调用class(参见 this 回答)。这将导致缓存在
之前未被清除
List<CouponType> couponTypeList = couponTypeRepository.getCoupons();
被调用。这意味着 @Cacheable
getCoupons()
方法不会查询数据库,而是会从缓存中获取 return 值。
这使得计划的缓存刷新操作仅在缓存为空时正常工作一次。之后就没用了。
@CacheEvict
注解应该移到refreshAllCaches()
方法中并添加beforeInvocation=true
参数,所以缓存在被之前被清除填充,而不是之后。
此外,当使用 Spring 4 / Spring 引导 1.X 时,应考虑这些错误:
虽然这个错误似乎不会影响这个特定的程序,但在迁移到 Spring 5 / Spring开机2.X.
我在 Tomcat 9.0.2 上使用 Spring Boot 1.5.9,我正在尝试使用 spring @Cacheable[=27= 缓存查找] 安排在应用程序启动时运行并每 24 小时重复一次的缓存刷新作业,如下所示:
@Component
public class RefreshCacheJob {
private static final Logger logger = LoggerFactory.getLogger(RefreshCacheJob.class);
@Autowired
private CacheService cacheService;
@Scheduled(fixedRate = 3600000 * 24, initialDelay = 0)
public void refreshCache() {
try {
cacheService.refreshAllCaches();
} catch (Exception e) {
logger.error("Exception in RefreshCacheJob", e);
}
}
}
缓存服务如下:
@Service
public class CacheService {
private static final Logger logger = LoggerFactory.getLogger(CacheService.class);
@Autowired
private CouponTypeRepository couponTypeRepository;
@CacheEvict(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES, allEntries = true)
public void clearCouponsTypesCache() {}
public void refreshAllCaches() {
clearCouponsTypesCache();
List<CouponType> couponTypeList = couponTypeRepository.getCoupons();
logger.info("######### couponTypeList: " + couponTypeList.size());
}
}
存储库代码:
public interface CouponTypeRepository extends JpaRepository<CouponType, BigInteger> {
@Query("from CouponType where active=true and expiryDate > CURRENT_DATE order by priority")
@Cacheable(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES)
List<CouponType> getCoupons();
}
稍后在我的网络服务中,当尝试按如下方式进行查找时:
@GET
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@Path("/getCoupons")
@ApiOperation(value = "")
public ServiceResponse getCoupons(@HeaderParam("token") String token, @HeaderParam("lang") String lang) throws Exception {
try {
List<CouponType> couponsList = couponRepository.getCoupons();
logger.info("###### couponsList: " + couponsList.size());
return new ServiceResponse(ErrorCodeEnum.SUCCESS_CODE, resultList, errorCodeRepository, lang);
} catch (Exception e) {
logger.error("Exception in getCoupons webservice: ", e);
return new ServiceResponse(ErrorCodeEnum.SYSTEM_ERROR_CODE, errorCodeRepository, lang);
}
}
第一次调用它从数据库中获取查找,随后的调用从缓存中获取,而它应该在 Web 服务中的第一次调用中从缓存中获取?
为什么我会出现这种行为,我该如何解决?
@CacheEvict
在同一服务中调用时不会被调用。这是因为 Spring 围绕服务创建了一个代理,只有来自 "outside" 的调用通过缓存代理。
解决方法是添加
@CacheEvict(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES, allEntries = true)
到 refreshAllCaches
,或者将 refreshAllCaches
移动到调用 ICacheService.clearCouponsTypeCache
.
升级到 Tomcat 9.0.4
后问题已解决虽然它本身不影响计划任务,但当在 CacheService
中调用 refreshAllCaches()
时,clearCouponsTypesCache()
上的 @CacheEvict
被绕过,因为它是从相同的调用class(参见 this 回答)。这将导致缓存在
List<CouponType> couponTypeList = couponTypeRepository.getCoupons();
被调用。这意味着 @Cacheable
getCoupons()
方法不会查询数据库,而是会从缓存中获取 return 值。
这使得计划的缓存刷新操作仅在缓存为空时正常工作一次。之后就没用了。
@CacheEvict
注解应该移到refreshAllCaches()
方法中并添加beforeInvocation=true
参数,所以缓存在被之前被清除填充,而不是之后。
此外,当使用 Spring 4 / Spring 引导 1.X 时,应考虑这些错误:
虽然这个错误似乎不会影响这个特定的程序,但在迁移到 Spring 5 / Spring开机2.X.