@Cacheable 注解在對象內部調用不會生效
代碼示例:
ProductServiceImpl.java
public List<ProductInfoVO> getProductList(CommonRequest<ProductInfoDTO> reqest) { // @Cacheable失效,不會走緩存的 return this.findProductInfoList(reqest); } @Cacheable(cacheNames = "productInfos",cacheManager="jfinetchRedisCacheManager", key = "'jbs:product:list:'.concat(#reqest.getChannelCode())") public List<ProductInfoVO> findProductInfoList(CommonRequest<ProductInfoDTO> reqest){ List<ProductInfoVO> redisList = productService.findList(reqest); redisList = redisList.stream().filter(it -> ProductStatusEnum.OPEN.getCode().equals(it.getStatus())).collect(Collectors.toList()); return redisList; }
此時getProductList 調用findProductInfoList緩存注解@Cacheable 是不會生效的。
原因:
Spring 緩存注解是基於Spring AOP切面,必須走代理才能生效,同類調用或者子類調用父類帶有緩存注解的方法時屬於內部調用,沒有走代理,所以注解不生效。
spring的@Transactional事務生效的一個前提是進行方法調用前經過攔截器TransactionInterceptor,也就是說只有通過TransactionInterceptor攔截器的方法才會被加入到spring事務管理中,查看spring源碼可以看到,在AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice方法中會從調用方法中獲取@Transactional注解,如果有該注解,則啟用事務,否則不啟用。
這個方法是通過spring的AOP類CglibAopProxy的內部類DynamicAdvisedInterceptor調用的,而DynamicAdvisedInterceptor繼承了MethodInterceptor,用於攔截方法調用,並從中獲取調用鏈。
這個方法是通過spring的AOP類CglibAopProxy的內部類DynamicAdvisedInterceptor調用的,而DynamicAdvisedInterceptor繼承了MethodInterceptor,用於攔截方法調用,並從中獲取調用鏈。
Transactional是Spring提供的事務管理注解。
重點在於,Spring采用動態代理(AOP)實現對bean的管理和切片,它為我們的每個class生成一個代理對象。只有在代理對象之間進行調用時,可以觸發切面邏輯。
而在同一個class中,方法B調用方法A,調用的是原對象的方法,而不通過代理對象。所以Spring無法切到這次調用,也就無法通過注解保證事務性了。
也就是說,在同一個類中的方法調用,則不會被方法攔截器攔截到,因此事務不會起作用。
解決方法
1.可以把removeGroupUserStatusCached方法單獨抽取到另外一個類里面,通過代理類引入。
2.通過((GroupUserService)AopContext.currentProxy()).removeGroupUserStatusCached方法獲取當前類的代理類;
3.通過ApplicationContext獲取當前類的代理對象,GroupUserService groupUserService = springContextUtil.getBean(GroupUserService.class);
可以將方法放入另一個類,並且該類通過spring注入,即符合了在對象之間調用的條件。
獲取本對象的代理對象,再進行調用。具體操作如:
Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),獲取到xxxService的代理類,再調用事務方法,強行經過代理類,激活事務切面。