Spring @Cacheable註解類內部調用失效的解決方案
@Cacheable註解類內部調用失效
如果你隻是想使用一個輕量級的緩存方案,那麼可以嘗試使用Spring cache方案。
那麼在使用spring @Cacheable註解的時候,要註意,如果類A的方法f()被標註瞭@Cacheable註解,那麼當類A的其他方法,例如:f2(),去直接調用f()的時候,@Cacheable是不起作用的,原因是@Cacheable是基於spring aop代理類,f2()屬於內部方法,直接調用f()時,是不走代理的。
舉個例子:
@Cacheable(key = "#entityType", value = "xxxCache") public List<String selectByEntityType(intentityType) { List<String> result = new ArrayList<>(); //do something return result; } public List<String> f2(){ //Cacheable失效,不會走緩存的 selectByEntityType(1); }
可以把selectByEntityType方法抽取到另外的類中,例如:
@Service public class CacheService{ @Cacheable(key = "#entityType", value = "xxxCache") public List<String selectByEntityType(intentityType) { List<String> result = new ArrayList<>(); //do something return result; } }
這樣其他類要使用selectByEntityType方法,隻能註入CacheService,走代理。
@Cacheable註解緩存方法內部調用
因為Spring Cache是基於切面的(基於AOP的動態代理實現的:即都在方法調用前後去獲取方法的名稱、參數、返回值,然後根據方法名稱、參數生成緩存的key(自定義的key例外),進行緩存),所以內部方法調用不會調用切面,導致緩存不生效
方法一
暴露Aop代理到ThreadLocal支持,在類之前加@EnableAspectJAutoProxy(exposeProxy = true)
調用的時候使用((XxxService) AopContext.currentProxy()).method()調用方法
eg:
ApiBaseResponse<ApiPageResponse<RoadCongestIndexData>> apiPageResponseApiBaseResponse = ((RoadLastPageServiceImpl) AopContext.currentProxy()).queryLastPageCongestIndexData1(request);
方法二
把需要用緩存的方法單獨寫到一個類裡面,把內部調用變成類間調用
RoadLastPageServiceImpl selfService = SpringContextUtil.getBean(RoadLastPageServiceImpl.class); selfService.queryLastPageCongestIndexData1(request);
方法三
類自我註入,使用@lazy和@Autowired註解實現自我註入,然後使用時用註解的實例代替this調用方法。
@Lazy @Autowired private RoadLastPageServiceImpl serviceImplCache;
方法四
寫一個工具類,使用內部調用的時候,自己實例化一個對象,讓類走AOP
@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; /** * 實現ApplicationContextAware接口的回調方法,設置上下文環境 * * @param applicationContext */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 獲取對象 * * @param name * @return Object * @throws BeansException */ public static Object getBean(String name) throws BeansException { return applicationContext.getBean(name); } /** * 通過類型獲取對象 * * @param t * 對象類型 * @return * @throws BeansException */ public static <T> T getBean(Class<T> t) throws BeansException { return applicationContext.getBean(t); } }
調用的時候這麼調用
RoadLastPageServiceImpl selfService = SpringContextUtil.getBean(RoadLastPageServiceImpl.class); selfService.queryLastPageCongestIndexData1(request);
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 獲取Spring的上下文環境ApplicationContext的最簡單方式
- 解決SpringMvc中普通類註入Service為null的問題
- SpringBoot項目如何將Bean註入到普通類中
- 關於Springboot如何獲取IOC容器
- SpringBoot中的main方法註入service