Spring @Cacheable注解 && 事務@Transactional 在同一個類中的方法調用不生效


@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的代理類,再調用事務方法,強行經過代理類,激活事務切面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM