我在 tomcat 9.0.2 上使用 Spring Boot 1.5.9 ,我正在尝试使用spring @Cacheable 调度a来缓存查找在应用程序启动时运行的缓存刷新作业,每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服务的第一次调用中从缓存中获取它?
请告知我为什么会遇到这种行为以及如何解决此问题。
答案 0 :(得分:2)
升级到 Tomcat 9.0.4
后问题已解决答案 1 :(得分:1)
虽然它不会影响计划任务本身,但在CacheService
中调用@CacheEvict
时,clearCouponsTypesCache()
上的List<CouponType> couponTypeList = couponTypeRepository.getCoupons();
被绕过,因为它是从同一个类调用的(见this回答)。它将导致缓存未被清除
@Cacheable
被调用。这意味着getCoupons()
@CacheEvict
方法不会查询数据库,而是返回缓存中的值。
这使得计划缓存刷新操作仅在缓存为空时才能正常工作一次。之后它没用了。
refreshAllCaches()
注释应移至beforeInvocation=true
方法并向其添加@Cacheable
参数,以便在填充之前清除缓存,而不是之后。
此外,使用Spring 4 / Spring Boot 1.X时,应考虑这些错误:
虽然此错误似乎不会影响此特定程序,但将JpaRepository
注释与{{1}}接口分开,直到迁移到Spring 5 / Spring Boot 2.X可能是个好主意。< / p>
答案 2 :(得分:0)
@CacheEvict
不会被调用。这是因为Spring在服务周围创建了一个代理服务器,只能从&#34;外部&#34;浏览缓存代理。
解决方案是添加
@CacheEvict(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES, allEntries = true)
至refreshAllCaches
,或将refreshAllCaches
移至调用ICacheService.clearCouponsTypeCache
的新服务。