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。

推薦閱讀: