序
本文主要研究一下JetCacheProxyConfiguration
JetCacheProxyConfiguration
com/alicp/jetcache/anno/config/JetCacheProxyConfiguration.java
@Configuration
public class JetCacheProxyConfiguration implements ImportAware, ApplicationContextAware {protected AnnotationAttributes enableMethodCache;private ApplicationContext applicationContext;@Overridepublic 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());}}@Overridepublic 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
public class JetCacheInterceptor implements MethodInterceptor, ApplicationContextAware {private static final Logger logger = LoggerFactory.getLogger(JetCacheInterceptor.class);@Autowiredprivate ConfigMap cacheConfigMap;private ApplicationContext applicationContext;private GlobalCacheConfig globalCacheConfig;ConfigProvider configProvider;CacheManager cacheManager;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic 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
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
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() {@Overridepublic Object load(Object k) throws Throwable {Object result = invokeOrigin(context);context.setResult(result);return result;}@Overridepublic 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
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
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
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
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
public class CacheAdvisor extends AbstractBeanFactoryPointcutAdvisor {public static final String CACHE_ADVISOR_BEAN_NAME = "jetcache2.internalCacheAdvisor";@Autowiredprivate ConfigMap cacheConfigMap;private String[] basePackages;@Overridepublic 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
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;}@Overridepublic 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;}@Overridepublic 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
@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