前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊JetCacheProxyConfiguration

聊聊JetCacheProxyConfiguration

作者头像
code4it
发布2024-06-19 20:19:36
950
发布2024-06-19 20:19:36
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究一下JetCacheProxyConfiguration

JetCacheProxyConfiguration

com/alicp/jetcache/anno/config/JetCacheProxyConfiguration.java

代码语言:javascript
复制
@Configuration
public class JetCacheProxyConfiguration implements ImportAware, ApplicationContextAware {

    protected AnnotationAttributes enableMethodCache;
    private ApplicationContext applicationContext;

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        this.enableMethodCache = AnnotationAttributes.fromMap(
                importMetadata.getAnnotationAttributes(EnableMethodCache.class.getName(), false));
        if (this.enableMethodCache == null) {
            throw new IllegalArgumentException(
                    "@EnableMethodCache is not present on importing class " + importMetadata.getClassName());
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Bean(name = CacheAdvisor.CACHE_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheAdvisor jetcacheAdvisor(JetCacheInterceptor jetCacheInterceptor) {
        CacheAdvisor advisor = new CacheAdvisor();
        advisor.setAdviceBeanName(CacheAdvisor.CACHE_ADVISOR_BEAN_NAME);
        advisor.setAdvice(jetCacheInterceptor);
        advisor.setBasePackages(this.enableMethodCache.getStringArray("basePackages"));
        advisor.setOrder(this.enableMethodCache.<Integer>getNumber("order"));
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public JetCacheInterceptor jetCacheInterceptor() {
        return new JetCacheInterceptor();
    }

}

JetCacheProxyConfiguration定义了JetCacheInterceptor、CacheAdvisor

JetCacheInterceptor

com/alicp/jetcache/anno/aop/JetCacheInterceptor.java

代码语言:javascript
复制
public class JetCacheInterceptor implements MethodInterceptor, ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(JetCacheInterceptor.class);

    @Autowired
    private ConfigMap cacheConfigMap;
    private ApplicationContext applicationContext;
    private GlobalCacheConfig globalCacheConfig;
    ConfigProvider configProvider;
    CacheManager cacheManager;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        if (configProvider == null) {
            configProvider = applicationContext.getBean(ConfigProvider.class);
        }
        if (configProvider != null && globalCacheConfig == null) {
            globalCacheConfig = configProvider.getGlobalCacheConfig();
        }
        if (globalCacheConfig == null || !globalCacheConfig.isEnableMethodCache()) {
            return invocation.proceed();
        }
        if (cacheManager == null) {
            cacheManager = applicationContext.getBean(CacheManager.class);
            if (cacheManager == null) {
                logger.error("There is no cache manager instance in spring context");
                return invocation.proceed();
            }
        }

        Method method = invocation.getMethod();
        Object obj = invocation.getThis();
        CacheInvokeConfig cac = null;
        if (obj != null) {
            String key = CachePointcut.getKey(method, obj.getClass());
            cac  = cacheConfigMap.getByMethodInfo(key);
        }

        /*
        if(logger.isTraceEnabled()){
            logger.trace("JetCacheInterceptor invoke. foundJetCacheConfig={}, method={}.{}(), targetClass={}",
                    cac != null,
                    method.getDeclaringClass().getName(),
                    method.getName(),
                    invocation.getThis() == null ? null : invocation.getThis().getClass().getName());
        }
        */

        if (cac == null || cac == CacheInvokeConfig.getNoCacheInvokeConfigInstance()) {
            return invocation.proceed();
        }

        CacheInvokeContext context = configProvider.newContext(cacheManager).createCacheInvokeContext(cacheConfigMap);
        context.setTargetObject(invocation.getThis());
        context.setInvoker(invocation::proceed);
        context.setMethod(method);
        context.setArgs(invocation.getArguments());
        context.setCacheInvokeConfig(cac);
        context.setHiddenPackages(globalCacheConfig.getHiddenPackages());
        return CacheHandler.invoke(context);
    }

    public void setCacheConfigMap(ConfigMap cacheConfigMap) {
        this.cacheConfigMap = cacheConfigMap;
    }

}

JetCacheInterceptor实现了aop的MethodInterceptor方法,其invoke方法先从spring中获取configProvider、globalCacheConfig,若globalCacheConfig为null或者是没有开启methodCache,则直接执行invocation.proceed(),否则获取cacheManager,然后从invocation获取key,从cacheConfigMap找到对应的CacheInvokeConfig,若为null则执行invocation.proceed(),否则通过configProvider.newContext(cacheManager).createCacheInvokeContext(cacheConfigMap)创建CacheInvokeContext,执行CacheHandler.invoke(context)

CacheHandler.invoke

com/alicp/jetcache/anno/method/CacheHandler.java

代码语言:javascript
复制
    public static Object invoke(CacheInvokeContext context) throws Throwable {
        if (context.getCacheInvokeConfig().isEnableCacheContext()) {
            try {
                CacheContextSupport._enable();
                return doInvoke(context);
            } finally {
                CacheContextSupport._disable();
            }
        } else {
            return doInvoke(context);
        }
    }

    private static Object doInvoke(CacheInvokeContext context) throws Throwable {
        CacheInvokeConfig cic = context.getCacheInvokeConfig();
        CachedAnnoConfig cachedConfig = cic.getCachedAnnoConfig();
        if (cachedConfig != null && (cachedConfig.isEnabled() || CacheContextSupport._isEnabled())) {
            return invokeWithCached(context);
        } else if (cic.getInvalidateAnnoConfigs() != null || cic.getUpdateAnnoConfig() != null) {
            return invokeWithInvalidateOrUpdate(context);
        } else {
            return invokeOrigin(context);
        }
    }   

    private static Object invokeOrigin(CacheInvokeContext context) throws Throwable {
        return context.getInvoker().invoke();
    }     

CacheHandler的invoke方法主要是执行doInvoke,它根据配置判断是走invokeWithCached、invokeWithInvalidateOrUpdate还是invokeOrigin

invokeWithCached

代码语言:javascript
复制
    private static Object invokeWithCached(CacheInvokeContext context)
            throws Throwable {
        CacheInvokeConfig cic = context.getCacheInvokeConfig();
        CachedAnnoConfig cac = cic.getCachedAnnoConfig();
        Cache cache = context.getCacheFunction().apply(context, cac);
        if (cache == null) {
            logger.error("no cache with name: " + context.getMethod());
            return invokeOrigin(context);
        }

        Object key = ExpressionUtil.evalKey(context, cic.getCachedAnnoConfig());
        if (key == null) {
            return loadAndCount(context, cache, key);
        }

        if (!ExpressionUtil.evalCondition(context, cic.getCachedAnnoConfig())) {
            return loadAndCount(context, cache, key);
        }

        try {
            CacheLoader loader = new CacheLoader() {
                @Override
                public Object load(Object k) throws Throwable {
                    Object result = invokeOrigin(context);
                    context.setResult(result);
                    return result;
                }

                @Override
                public boolean vetoCacheUpdate() {
                    return !ExpressionUtil.evalPostCondition(context, cic.getCachedAnnoConfig());
                }
            };
            Object result = cache.computeIfAbsent(key, loader);
            return result;
        } catch (CacheInvokeException e) {
            throw e.getCause();
        }
    }

invokeWithCached先获取key,若key为null或者不满足表达式则执行loadAndCount,否则执行cache.computeIfAbsent(key, loader)

loadAndCount

代码语言:javascript
复制
    private static Object loadAndCount(CacheInvokeContext context, Cache cache, Object key) throws Throwable {
        long t = System.currentTimeMillis();
        Object v = null;
        boolean success = false;
        try {
            v = invokeOrigin(context);
            success = true;
        } finally {
            t = System.currentTimeMillis() - t;
            CacheLoadEvent event = new CacheLoadEvent(cache, t, key, v, success);
            while (cache instanceof ProxyCache) {
                cache = ((ProxyCache) cache).getTargetCache();
            }
            if (cache instanceof AbstractCache) {
                ((AbstractCache) cache).notify(event);
            }
        }
        return v;
    }

loadAndCount主要是执行invokeOrigin,然后发布CacheLoadEvent事件

invokeWithInvalidateOrUpdate

代码语言:javascript
复制
    private static Object invokeWithInvalidateOrUpdate(CacheInvokeContext context) throws Throwable {
        Object originResult = invokeOrigin(context);
        context.setResult(originResult);
        CacheInvokeConfig cic = context.getCacheInvokeConfig();

        if (cic.getInvalidateAnnoConfigs() != null) {
            doInvalidate(context, cic.getInvalidateAnnoConfigs());
        }
        CacheUpdateAnnoConfig updateAnnoConfig = cic.getUpdateAnnoConfig();
        if (updateAnnoConfig != null) {
            doUpdate(context, updateAnnoConfig);
        }

        return originResult;
    }

invokeWithInvalidateOrUpdate先执行invokeOrigin,然后根据判断执行doInvalidate或者doUpdate

CacheFunction

CacheInvokeContext

com/alicp/jetcache/anno/method/CacheInvokeContext.java

代码语言:javascript
复制
	private BiFunction<CacheInvokeContext, CacheAnnoConfig, Cache> cacheFunction;

    public void setCacheFunction(BiFunction<CacheInvokeContext, CacheAnnoConfig, Cache> cacheFunction) {
        this.cacheFunction = cacheFunction;
    }

    public BiFunction<CacheInvokeContext, CacheAnnoConfig, Cache> getCacheFunction() {
        return cacheFunction;
    }

CacheInvokeContext定义了cacheFunction,是一个BiFunction,第一个参数为CacheInvokeContext,第二个参数为CacheAnnoConfig,返回类型为Cache

createCacheInvokeContext

com/alicp/jetcache/anno/support/CacheContext.java

代码语言:javascript
复制
    public CacheInvokeContext createCacheInvokeContext(ConfigMap configMap) {
        CacheInvokeContext c = newCacheInvokeContext();
        c.setCacheFunction((cic, cac) -> createOrGetCache(cic, cac, configMap));
        return c;
    }

    protected CacheInvokeContext newCacheInvokeContext() {
        return new CacheInvokeContext();
    }    

    private Cache createOrGetCache(CacheInvokeContext invokeContext, CacheAnnoConfig cacheAnnoConfig, ConfigMap configMap) {
        Cache cache = cacheAnnoConfig.getCache();
        if (cache != null) {
            return cache;
        }
        if (cacheAnnoConfig instanceof CachedAnnoConfig) {
            cache = createCacheByCachedConfig((CachedAnnoConfig) cacheAnnoConfig, invokeContext);
        } else if ((cacheAnnoConfig instanceof CacheInvalidateAnnoConfig) || (cacheAnnoConfig instanceof CacheUpdateAnnoConfig)) {
            cache = cacheManager.getCache(cacheAnnoConfig.getArea(), cacheAnnoConfig.getName());
            if (cache == null) {
                CachedAnnoConfig cac = configMap.getByCacheName(cacheAnnoConfig.getArea(), cacheAnnoConfig.getName());
                if (cac == null) {
                    String message = "can't find cache definition with area=" + cacheAnnoConfig.getArea()
                            + " name=" + cacheAnnoConfig.getName() +
                            ", specified in " + cacheAnnoConfig.getDefineMethod();
                    CacheConfigException e = new CacheConfigException(message);
                    logger.error("Cache operation aborted because can't find cached definition", e);
                    return null;
                }
                cache = createCacheByCachedConfig(cac, invokeContext);
            }
        }
        cacheAnnoConfig.setCache(cache);
        return cache;
    } 

    private Cache createCacheByCachedConfig(CachedAnnoConfig ac, CacheInvokeContext invokeContext) {
        String area = ac.getArea();
        String cacheName = ac.getName();
        if (CacheConsts.isUndefined(cacheName)) {

            cacheName = configProvider.createCacheNameGenerator(invokeContext.getHiddenPackages())
                    .generateCacheName(invokeContext.getMethod(), invokeContext.getTargetObject());
        }
        Cache cache = __createOrGetCache(ac, area, cacheName);
        return cache;
    }

    public Cache __createOrGetCache(CachedAnnoConfig cac, String area, String cacheName) {
        QuickConfig.Builder b = QuickConfig.newBuilder(area, cacheName);
        TimeUnit timeUnit = cac.getTimeUnit();
        if (cac.getExpire() > 0) {
            b.expire(Duration.ofMillis(timeUnit.toMillis(cac.getExpire())));
        }
        if (cac.getLocalExpire() > 0) {
            b.localExpire(Duration.ofMillis(timeUnit.toMillis(cac.getLocalExpire())));
        }
        if (cac.getLocalLimit() > 0) {
            b.localLimit(cac.getLocalLimit());
        }
        b.cacheType(cac.getCacheType());
        b.syncLocal(cac.isSyncLocal());
        if (!CacheConsts.isUndefined(cac.getKeyConvertor())) {
            b.keyConvertor(configProvider.parseKeyConvertor(cac.getKeyConvertor()));
        }
        if (!CacheConsts.isUndefined(cac.getSerialPolicy())) {
            b.valueEncoder(configProvider.parseValueEncoder(cac.getSerialPolicy()));
            b.valueDecoder(configProvider.parseValueDecoder(cac.getSerialPolicy()));
        }
        b.cacheNullValue(cac.isCacheNullValue());
        b.useAreaInPrefix(globalCacheConfig.isAreaInCacheName());
        PenetrationProtectConfig ppc = cac.getPenetrationProtectConfig();
        if (ppc != null) {
            b.penetrationProtect(ppc.isPenetrationProtect());
            b.penetrationProtectTimeout(ppc.getPenetrationProtectTimeout());
        }
        b.refreshPolicy(cac.getRefreshPolicy());
        return cacheManager.getOrCreateCache(b.build());
    }           

createCacheInvokeContext方法会设置cacheFunction为(cic, cac) -> createOrGetCache(cic, cac, configMap),通过createOrGetCache来创建或者获取Cache;createOrGetCache从cacheAnnoConfig.getCache()获取Cache,不为null则返回,为null则通过createCacheByCachedConfig来创建,其最后是创建QuickConfig,然后通过cacheManager.getOrCreateCache(b.build())来创建Cache

CacheAdvisor

com/alicp/jetcache/anno/aop/CacheAdvisor.java

代码语言:javascript
复制
public class CacheAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    public static final String CACHE_ADVISOR_BEAN_NAME = "jetcache2.internalCacheAdvisor";

    @Autowired
    private ConfigMap cacheConfigMap;

    private String[] basePackages;

    @Override
    public Pointcut getPointcut() {
        CachePointcut pointcut = new CachePointcut(basePackages);
        pointcut.setCacheConfigMap(cacheConfigMap);
        return pointcut;
    }

    public void setCacheConfigMap(ConfigMap cacheConfigMap) {
        this.cacheConfigMap = cacheConfigMap;
    }

    public void setBasePackages(String[] basePackages) {
        this.basePackages = basePackages;
    }
}

CacheAdvisor继承了spring的AbstractBeanFactoryPointcutAdvisor,其getPointcut返回的是CachePointcut

CachePointcut

com/alicp/jetcache/anno/aop/CachePointcut.java

代码语言:javascript
复制
public class CachePointcut extends StaticMethodMatcherPointcut implements ClassFilter {

    private static final Logger logger = LoggerFactory.getLogger(CachePointcut.class);

    private ConfigMap cacheConfigMap;
    private String[] basePackages;

    public CachePointcut(String[] basePackages) {
        setClassFilter(this);
        this.basePackages = basePackages;
    }

    @Override
    public boolean matches(Class clazz) {
        boolean b = matchesImpl(clazz);
        logger.trace("check class match {}: {}", b, clazz);
        return b;
    }

    private boolean matchesImpl(Class clazz) {
        if (matchesThis(clazz)) {
            return true;
        }
        Class[] cs = clazz.getInterfaces();
        if (cs != null) {
            for (Class c : cs) {
                if (matchesImpl(c)) {
                    return true;
                }
            }
        }
        if (!clazz.isInterface()) {
            Class sp = clazz.getSuperclass();
            if (sp != null && matchesImpl(sp)) {
                return true;
            }
        }
        return false;
    }

    public boolean matchesThis(Class clazz) {
        String name = clazz.getName();
        if (exclude(name)) {
            return false;
        }
        return include(name);
    }

    private boolean include(String name) {
        if (basePackages != null) {
            for (String p : basePackages) {
                if (name.startsWith(p)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean exclude(String name) {
        if (name.startsWith("java")) {
            return true;
        }
        if (name.startsWith("org.springframework")) {
            return true;
        }
        if (name.indexOf("$$EnhancerBySpringCGLIB$$") >= 0) {
            return true;
        }
        if (name.indexOf("$$FastClassBySpringCGLIB$$") >= 0) {
            return true;
        }
        return false;
    }

    @Override
    public boolean matches(Method method, Class targetClass) {
        boolean b = matchesImpl(method, targetClass);
        if (b) {
            if (logger.isDebugEnabled()) {
                logger.debug("check method match true: method={}, declaringClass={}, targetClass={}",
                        method.getName(),
                        ClassUtil.getShortClassName(method.getDeclaringClass().getName()),
                        targetClass == null ? null : ClassUtil.getShortClassName(targetClass.getName()));
            }
        } else {
            if (logger.isTraceEnabled()) {
                logger.trace("check method match false: method={}, declaringClass={}, targetClass={}",
                        method.getName(),
                        ClassUtil.getShortClassName(method.getDeclaringClass().getName()),
                        targetClass == null ? null : ClassUtil.getShortClassName(targetClass.getName()));
            }
        }
        return b;
    }

    private boolean matchesImpl(Method method, Class targetClass) {
        if (!matchesThis(method.getDeclaringClass())) {
            return false;
        }
        if (exclude(targetClass.getName())) {
            return false;
        }
        String key = getKey(method, targetClass);
        CacheInvokeConfig cac = cacheConfigMap.getByMethodInfo(key);
        if (cac == CacheInvokeConfig.getNoCacheInvokeConfigInstance()) {
            return false;
        } else if (cac != null) {
            return true;
        } else {
            cac = new CacheInvokeConfig();
            CacheConfigUtil.parse(cac, method);

            String name = method.getName();
            Class<?>[] paramTypes = method.getParameterTypes();
            parseByTargetClass(cac, targetClass, name, paramTypes);

            if (!cac.isEnableCacheContext() && cac.getCachedAnnoConfig() == null &&
                    cac.getInvalidateAnnoConfigs() == null && cac.getUpdateAnnoConfig() == null) {
                cacheConfigMap.putByMethodInfo(key, CacheInvokeConfig.getNoCacheInvokeConfigInstance());
                return false;
            } else {
                cacheConfigMap.putByMethodInfo(key, cac);
                return true;
            }
        }
    }

    public static String getKey(Method method, Class targetClass) {
        StringBuilder sb = new StringBuilder();
        sb.append(method.getDeclaringClass().getName());
        sb.append('.');
        sb.append(method.getName());
        sb.append(Type.getMethodDescriptor(method));
        if (targetClass != null) {
            sb.append('_');
            sb.append(targetClass.getName());
        }
        return sb.toString();
    }

    private void parseByTargetClass(CacheInvokeConfig cac, Class<?> clazz, String name, Class<?>[] paramTypes) {
        if (!clazz.isInterface() && clazz.getSuperclass() != null) {
            parseByTargetClass(cac, clazz.getSuperclass(), name, paramTypes);
        }
        Class<?>[] intfs = clazz.getInterfaces();
        for (Class<?> it : intfs) {
            parseByTargetClass(cac, it, name, paramTypes);
        }

        boolean matchThis = matchesThis(clazz);
        if (matchThis) {
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (methodMatch(name, method, paramTypes)) {
                    CacheConfigUtil.parse(cac, method);
                    break;
                }
            }
        }
    }

    private boolean methodMatch(String name, Method method, Class<?>[] paramTypes) {
        if (!Modifier.isPublic(method.getModifiers())) {
            return false;
        }
        if (!name.equals(method.getName())) {
            return false;
        }
        Class<?>[] ps = method.getParameterTypes();
        if (ps.length != paramTypes.length) {
            return false;
        }
        for (int i = 0; i < ps.length; i++) {
            if (!ps[i].equals(paramTypes[i])) {
                return false;
            }
        }
        return true;
    }


    public void setCacheConfigMap(ConfigMap cacheConfigMap) {
        this.cacheConfigMap = cacheConfigMap;
    }
}

CachePointcut继承了spring的StaticMethodMatcherPointcut,其matches方法委托给了matchesImpl,它主要是通过cacheConfigMap.getByMethodInfo(key)来获取CacheInvokeConfig,如果是noCacheInvokeConfigInstance则返回false,若不为null则返回true,若为null则创建CacheInvokeConfig维护到cacheConfigMap中

CommonConfiguration

com/alicp/jetcache/anno/config/CommonConfiguration.java

代码语言:javascript
复制
@Configuration
public class CommonConfiguration {
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ConfigMap jetcacheConfigMap() {
        return new ConfigMap();
    }
}

CommonConfiguration创建ConfigMap实例

小结

JetCacheProxyConfiguration主要是定义了JetCacheInterceptor、CacheAdvisor,其中JetCacheInterceptor实现了aop的MethodInterceptor方法,其invoke方法主要是通过configProvider.newContext(cacheManager).createCacheInvokeContext(cacheConfigMap)创建CacheInvokeContext,执行CacheHandler.invoke(context)通过context.getCacheFunction().apply(context, cac)获取com.alicp.jetcache.Cache,然后进行相关操作;CachePointcut继承了spring的StaticMethodMatcherPointcut,其matches方法委托给了matchesImpl,它主要是通过cacheConfigMap.getByMethodInfo(key)来获取CacheInvokeConfig,如果是noCacheInvokeConfigInstance则返回false,若不为null则返回true,若为null则创建CacheInvokeConfig维护到cacheConfigMap中。

doc

  • jetcache
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JetCacheProxyConfiguration
  • JetCacheInterceptor
    • CacheHandler.invoke
      • invokeWithCached
        • loadAndCount
          • invokeWithInvalidateOrUpdate
          • CacheFunction
            • CacheInvokeContext
              • createCacheInvokeContext
              • CacheAdvisor
                • CachePointcut
                  • CommonConfiguration
                  • 小结
                  • doc
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档