@Cacheable 注解在对象内部调用不会生效
代码示例:
ProductServiceImpl.java
public ListgetProductList(CommonRequest reqest) { // @Cacheable失效,不会走缓存的 return this.findProductInfoList(reqest); } @Cacheable(cacheNames = "productInfos",cacheManager="jfinetchRedisCacheManager", key = "'jbs:product:list:'.concat(#reqest.getChannelCode())") public List findProductInfoList(CommonRequest reqest){ List 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上下文中,增加配置:
在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),获取到xxxService的代理类,再调用事务方法,强行经过代理类,激活事务切面。