作者:京東物流 方志民
一. 背景
在一次系統(tǒng)opsreview中,發(fā)現(xiàn)了一些服務(wù)配置了@Cacheable注解。@cacheable 來(lái)源于spring cache框架中,作用是使用aop的方式將數(shù)據(jù)庫(kù)中的熱數(shù)據(jù)緩存在redis/本地緩存中,代碼如下:
@Cacheable(value = { "per" }, key="#person.getId()"+"_"+"#person.getName()") public Person getByIsbn(Person person) { return personMapper.getPerson(person); }
那么這個(gè)原生spring組件是如何工作的?redis的key是如何產(chǎn)生的?這一過(guò)程是否還有優(yōu)化的空間?帶著這些問(wèn)題我們來(lái)開(kāi)啟源碼之旅。
二. Spring@Cacheable注解工作原理
就以項(xiàng)目中使用的spring3.2.18版本為例分析,代碼中使用了xml+cache標(biāo)簽的形式去啟動(dòng)注解緩存。然而在springboot中使用的是@EnableCaching注解,通過(guò)自動(dòng)配置加載相關(guān)組件,兩種方式都是殊途同歸,這里就不做贅述了,直接上鏈接。
首先,如果我們想使用這個(gè)組件就需要先啟用緩存注解,方式與aop功能相類似,aop也會(huì)加載internalAutoProxyCreator后置處理器。代碼中通過(guò)annotation-driven標(biāo)簽加載相關(guān)組件。其中proxy-target-class="true" 表示使用CGLIB的方式對(duì)bean進(jìn)行動(dòng)態(tài)代理。
// !-- 添加緩存注解支持 --?> !-- 開(kāi)啟aop切面 --?>
代碼中cache-manager表示需要依賴一個(gè)緩存管理器,它的作用是提供一種機(jī)制來(lái)緩存數(shù)據(jù),以便在后續(xù)的訪問(wèn)中可以更快地獲取數(shù)據(jù)。它可以支持caffine,encache,Jcache等多種類型的緩存管理器。文中是使用的自定義管理來(lái)支持公司內(nèi)部的redis客戶端。
//redis緩存管理器 public class RedisCacheManager extends AbstractTransactionSupportingCacheManager { private Collection? extends Cache?> caches; public void setCaches(List caches) { this.caches = caches; } @Override protected Collection? extends Cache?> loadCaches() { if (caches == null) { return Collections.emptyList(); } return caches; } @Override public Cache getCache(String name) { Cache cache = super.getCache(name); if (cache == null && (cache = super.getCache("DEFAULT")) == null) { throw new NullPointerException(); } return cache; } }
下面通過(guò)bean的方式注入cacheManager管理器,其中MyCache需要實(shí)現(xiàn)org.springframework.cache.Cache中定義的方法,以達(dá)到手動(dòng)diy緩存操作的目的。
/list?> /property?> /bean?>
Cache接口中有g(shù)et,put,evict等方法,可以按需替換成自己想要的操作。
public interface Cache { String getName(); Object getNativeCache(); Cache.ValueWrapper get(Object var1); void put(Object var1, Object var2); void evict(Object var1); void clear(); public interface ValueWrapper { Object get(); } }
配置輸出完了,開(kāi)始切入正題。spring容器啟動(dòng)時(shí)候會(huì)解析annotation-driven標(biāo)簽,具體的實(shí)現(xiàn)在CacheNamespaceHandler中。顯然可以發(fā)現(xiàn)beanDefinition解析類是AnnotationDrivenCacheBeanDefinitionParser。
public class CacheNamespaceHandler extends NamespaceHandlerSupport { static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager"; static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager"; public CacheNamespaceHandler() { } static String extractCacheManager(Element element) { return element.hasAttribute("cache-manager") ? element.getAttribute("cache-manager") : "cacheManager"; } static BeanDefinition parseKeyGenerator(Element element, BeanDefinition def) { String name = element.getAttribute("key-generator"); if (StringUtils.hasText(name)) { def.getPropertyValues().add("keyGenerator", new RuntimeBeanReference(name.trim())); } return def; } public void init() { this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser()); this.registerBeanDefinitionParser("advice", new CacheAdviceParser()); } }
AnnotationDrivenCacheBeanDefinitionParser中會(huì)先判斷生成切面的方式,默認(rèn)使用spring原生aop,也可以通過(guò)mode標(biāo)簽切換成AspectJ。
public BeanDefinition parse(Element element, ParserContext parserContext) { String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { this.registerCacheAspect(element, parserContext); } else { AnnotationDrivenCacheBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null; }
往下走會(huì)到達(dá)configureAutoProxyCreator方法,configureAutoProxyCreator方法的作用是配置自動(dòng)代理創(chuàng)建器。代碼很多繼續(xù)往下看~
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); if (!parserContext.getRegistry().containsBeanDefinition("org.springframework.cache.config.internalCacheAdvisor")) { Object eleSource = parserContext.extractSource(element); RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.cache.annotation.AnnotationCacheOperationSource"); sourceDef.setSource(eleSource); sourceDef.setRole(2); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(2); AnnotationDrivenCacheBeanDefinitionParser.parseCacheManagerProperty(element, interceptorDef); CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef); interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName)); String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheOperationSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(2); advisorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } parserContext.getRegistry().registerBeanDefinition("org.springframework.cache.config.internalCacheAdvisor", advisorDef); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource); compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName)); compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName)); compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, "org.springframework.cache.config.internalCacheAdvisor")); parserContext.registerComponent(compositeDef); } }
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element)作用是注冊(cè)動(dòng)態(tài)代理創(chuàng)建器。跳轉(zhuǎn)兩次到達(dá)這個(gè)registerOrEscalateApcAsRequired方法,它會(huì)檢查是否存在org.springframework.aop.config.internalAutoProxyCreator的beanDefinition。
大概意思就是檢查此前是否還有其他的代理比如aop代理,它也會(huì)加載internalAutoProxyCreator這個(gè)后置處理器。如果已經(jīng)加載過(guò)internalAutoProxyCreator,則根據(jù)自動(dòng)代理創(chuàng)建器的優(yōu)先級(jí)判斷,使用優(yōu)先級(jí)高者。然后返回internalAutoProxyCreator的beanDefinition。
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) { BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator"); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } else { RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", -2147483648); beanDefinition.setRole(2); registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition); return beanDefinition; } }
書接上文,獲取beanDefinition后,會(huì)根據(jù)配置查看bean代理生成使用哪種模式,上文提到了,這里會(huì)根據(jù)proxy-target-class屬性做判斷,如果為true則使用CGLIB。添加屬性配置后會(huì)調(diào)用registerComponentIfNecessary重新注冊(cè)internalAutoProxyCreator組件。
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) { if (sourceElement != null) { boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute("proxy-target-class")); if (proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute("expose-proxy")); if (exposeProxy) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) { if (beanDefinition != null) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(beanDefinition, "org.springframework.aop.config.internalAutoProxyCreator"); parserContext.registerComponent(componentDefinition); } }
回到主流程中首先判斷是否加載過(guò)org.springframework.cache.config.internalCacheAdvisor目的是避免重復(fù)。校驗(yàn)過(guò)后定義了AnnotationCacheOperationSource這個(gè)beanDefinition,這個(gè)類比較繞,通過(guò)上帝視角總結(jié)下,它的作用是解析目標(biāo)方法中包含了哪些緩存操作, 比如Cacheable等注解。后面會(huì)作為其他bean的成員變量。
RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.cache.annotation.AnnotationCacheOperationSource"); sourceDef.setSource(eleSource); sourceDef.setRole(2); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
接下來(lái),是CacheInterceptor類的beanDefinition注冊(cè)。CacheInterceptor實(shí)現(xiàn)了aop的MethodInterceptor接口,我們可以叫他代理中的代理。。。
創(chuàng)建beanDefinition后將前文中AnnotationCacheOperationSource解析器作為配置項(xiàng)添加到CacheInterceptor的bean定義中。
RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(2); //這塊不特別說(shuō)明了,目的是為了添加cacheManager ref AnnotationDrivenCacheBeanDefinitionParser.parseCacheManagerProperty(element, interceptorDef); //設(shè)置KeyGenerator,不夠靈活pass掉了 CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef); // interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));
CacheInterceptor實(shí)際的作用是為配置@Cacheable注解的目標(biāo)方法提供切面功能,非常類似于一個(gè)定制化的@around。直接上代碼。通過(guò)上面的解析器獲取出緩存操作列表,如果能獲取到緩存且不需要更新緩存則直接返回?cái)?shù)據(jù)。如果需要更新則通過(guò)目標(biāo)方法獲取最新數(shù)據(jù),在刷新緩存后直接返回。在這里包含了生成rediskey的步驟,后面會(huì)有介紹。
protected Object execute(CacheAspectSupport.Invoker invoker, Object target, Method method, Object[] args) { if (!this.initialized) { return invoker.invoke(); } else { Class??> targetClass = AopProxyUtils.ultimateTargetClass(target); if (targetClass == null && target != null) { targetClass = target.getClass(); } Collection cacheOp = this.getCacheOperationSource().getCacheOperations(method, targetClass); if (!CollectionUtils.isEmpty(cacheOp)) { Map> ops = this.createOperationContext(cacheOp, method, args, target, targetClass); this.inspectBeforeCacheEvicts((Collection)ops.get("cacheevict")); CacheAspectSupport.CacheStatus status = this.inspectCacheables((Collection)ops.get("cacheable")); Map updates = this.inspectCacheUpdates((Collection)ops.get("cacheupdate")); if (status != null) { if (!status.updateRequired) { return status.retVal; } updates.putAll(status.cacheUpdates); } Object retVal = invoker.invoke(); this.inspectAfterCacheEvicts((Collection)ops.get("cacheevict"), retVal); if (!updates.isEmpty()) { this.update(updates, retVal); } return retVal; } else { return invoker.invoke(); } } }
返回主流程,下面這部分是BeanFactoryCacheOperationSourceAdvisor緩存通知器的beanDefinition。這個(gè)類功能是注冊(cè)aop,聲明了切面的連接點(diǎn)(實(shí)際上依賴于上文中cacheOperationSource這個(gè)bean)與通知(實(shí)際上依賴于上文中CacheInterceptor這個(gè)bean)。
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheOperationSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(2); advisorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } parserContext.getRegistry().registerBeanDefinition("org.springframework.cache.config.internalCacheAdvisor", advisorDef);
BeanFactoryCacheOperationSourceAdvisor類實(shí)現(xiàn)了PointcutAdvisor指定了切面點(diǎn)(實(shí)際沒(méi)用表達(dá)式,直接通過(guò)match暴力獲取注解,能獲取到則表示命中aop)
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor { private CacheOperationSource cacheOperationSource; private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() { protected CacheOperationSource getCacheOperationSource() { return BeanFactoryCacheOperationSourceAdvisor.this.cacheOperationSource; } }; public BeanFactoryCacheOperationSourceAdvisor() { } public void setCacheOperationSource(CacheOperationSource cacheOperationSource) { this.cacheOperationSource = cacheOperationSource; } public void setClassFilter(ClassFilter classFilter) { this.pointcut.setClassFilter(classFilter); } public Pointcut getPointcut() { return this.pointcut; } } //其中切面點(diǎn)matchs方法 public boolean matches(Method method, Class??> targetClass) { CacheOperationSource cas = this.getCacheOperationSource(); return cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)); }
最后,注冊(cè)復(fù)合組件,并將其注冊(cè)到解析器上下文中。熟悉aop源碼就可以知道,在bean實(shí)例化階段,后置處理器會(huì)檢查bean命中了哪個(gè)aop,再根據(jù)自動(dòng)代理生成器中的配置,來(lái)決定使用哪種代理方式生成代理類,同時(shí)織入對(duì)應(yīng)的advice。實(shí)際上是代理到CacheInterceptor上面,CacheInterceptor中間商內(nèi)部再調(diào)用target目標(biāo)類,就是這么簡(jiǎn)單~
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource); compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName)); compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName)); compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, "org.springframework.cache.config.internalCacheAdvisor")); parserContext.registerComponent(compositeDef);
三. 緩存key生成原理
然而key是如何產(chǎn)生的?通過(guò)上問(wèn)的闡述,就知道要找這個(gè)中間商CacheInterceptor,上代碼。
protected Object execute(CacheAspectSupport.Invoker invoker, Object target, Method method, Object[] args) { if (!this.initialized) { return invoker.invoke(); } else { Class??> targetClass = AopProxyUtils.ultimateTargetClass(target); if (targetClass == null && target != null) { targetClass = target.getClass(); } Collection cacheOp = this.getCacheOperationSource().getCacheOperations(method, targetClass); if (!CollectionUtils.isEmpty(cacheOp)) { Map> ops = this.createOperationContext(cacheOp, method, args, target, targetClass); this.inspectBeforeCacheEvicts((Collection)ops.get("cacheevict")); CacheAspectSupport.CacheStatus status = this.inspectCacheables((Collection)ops.get("cacheable")); Map updates = this.inspectCacheUpdates((Collection)ops.get("cacheupdate")); if (status != null) { if (!status.updateRequired) { return status.retVal; } updates.putAll(status.cacheUpdates); } Object retVal = invoker.invoke(); this.inspectAfterCacheEvicts((Collection)ops.get("cacheevict"), retVal); if (!updates.isEmpty()) { this.update(updates, retVal); } return retVal; } else { return invoker.invoke(); } } }
倒車回到這里,最直觀的嫌疑人是return status.retVal;這句繼續(xù)跟進(jìn)status。
private CacheAspectSupport.CacheStatus inspectCacheables(Collection cacheables) { Map cacheUpdates = new LinkedHashMap(cacheables.size()); boolean cacheHit = false; Object retVal = null; if (!cacheables.isEmpty()) { boolean log = this.logger.isTraceEnabled(); boolean atLeastOnePassed = false; Iterator i$ = cacheables.iterator(); while(true) { while(true) { CacheAspectSupport.CacheOperationContext context; Object key; label48: do { while(i$.hasNext()) { context = (CacheAspectSupport.CacheOperationContext)i$.next(); if (context.isConditionPassing()) { atLeastOnePassed = true; key = context.generateKey(); if (log) { this.logger.trace("Computed cache key " + key + " for operation " + context.operation); } if (key == null) { throw new IllegalArgumentException("Null key returned for cache operation (maybe you are using named params on classes without debug info?) " + context.operation); } cacheUpdates.put(context, key); continue label48; } if (log) { this.logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation); } } if (atLeastOnePassed) { return new CacheAspectSupport.CacheStatus(cacheUpdates, !cacheHit, retVal); } return null; } while(cacheHit); Iterator i$ = context.getCaches().iterator(); while(i$.hasNext()) { Cache cache = (Cache)i$.next(); ValueWrapper wrapper = cache.get(key); if (wrapper != null) { retVal = wrapper.get(); cacheHit = true; break; } } } } } else { return null; } }
key = context.generateKey(); 再跳轉(zhuǎn)。
protected Object generateKey() { if (StringUtils.hasText(this.operation.getKey())) { EvaluationContext evaluationContext = this.createEvaluationContext(ExpressionEvaluator.NO_RESULT); return CacheAspectSupport.this.evaluator.key(this.operation.getKey(), this.method, evaluationContext); } else { return CacheAspectSupport.this.keyGenerator.generate(this.target, this.method, this.args); } }
到達(dá)getExpression方法,由于key在注解上面配置了,所以不為空,在繼續(xù)跳轉(zhuǎn)。
public Object key(String keyExpression, Method method, EvaluationContext evalContext) { return this.getExpression(this.keyCache, keyExpression, method).getValue(evalContext); } private Expression getExpression(Map cache, String expression, Method method) { String key = this.toString(method, expression); Expression rtn = (Expression)cache.get(key); if (rtn == null) { rtn = this.parser.parseExpression(expression); cache.put(key, rtn); } return rtn; }
最終來(lái)到了parser.parseExpression;
根據(jù)代碼可以看到解析器用的是 private final SpelExpressionParser parser = new SpelExpressionParser();
可以得出結(jié)論就是Spel表達(dá)式這個(gè)東東吧。對(duì)于實(shí)體類+方法的表達(dá)式可能會(huì)實(shí)時(shí)去反射得到結(jié)果。那我們能不能再生產(chǎn)key的上層再加一層緩存呢?答案是肯定的。
四. 代碼優(yōu)化
我們可以通過(guò)javaPoet方式動(dòng)態(tài)生成class的形式,將生成的類加載到內(nèi)存中。通過(guò)它的實(shí)例來(lái)生成key。
javaPoet類似于javasis是一個(gè)用于動(dòng)態(tài)生成代碼的開(kāi)源項(xiàng)目,通過(guò)這個(gè)類庫(kù)下面的api我們來(lái)進(jìn)行簡(jiǎn)易diy嘗試。
上代碼,忽略不重要部分,切面簡(jiǎn)寫直接展示生成key的部分。
@Aspect @Component public class CacheAspect { @Around("@annotation(myCache)") public Object around(ProceedingJoinPoint pjp, MyCache myCache) throws Throwable { long currentTime = System.currentTimeMillis(); Object value = null; try { if(!myCache.useCache()){ return pjp.proceed(); } Object[] args = pjp.getArgs(); if(args == null || args[0] == null){ return pjp.proceed(); } Object obj = args[0]; String key = MyCacheCacheKeyGenerator.generatorCacheKey(myCache,obj.getClass().getDeclaredFields(),obj); ...... } catch (Throwable throwable) { log.error("cache throwable",throwable); } return pjp.proceed(); } }
緩存key生成接口。
public interface MyCacheKeyGenerator { /** * 生成key * */ String generateKey(Method method, Object[] args, Object target, String key); }
具體實(shí)現(xiàn),其中wrapper是一個(gè)包裝類,只是一個(gè)搬運(yùn)工。通過(guò)key來(lái)動(dòng)態(tài)產(chǎn)生key生成器。
public class DyCacheKeyGenerator implements MyCacheKeyGenerator { private final ConcurrentMap cacheMap = new ConcurrentHashMap(); /** * 生成key * * @param method 調(diào)用的方法名字 * @param args 參數(shù)列表 * @param target 目標(biāo)值 * @param key key的格式 * @return */ @Override public String generateKey(Method method, Object[] args, Object target, String key) { Wrapper wrapper = cacheMap.computeIfAbsent(key, k -> new Wrapper()); getMykeyGenerator(method, key, wrapper); return ((MyCacheKeyGenerator) wrapper.getData()).generate(args); } private void getMykeyGenerator(Method method, String key, Wrapper wrapper) { if (wrapper.getData() != null) { return; } synchronized (wrapper) { if (wrapper.getData() == null) { MyCacheKeyGenerator keyGenerator = MyCacheKeyGenerator.initMyKeyGenerator(method, key); wrapper.setData(keyGenerator); } } } }
那么我們首先根據(jù)key獲取表達(dá)式的集合,如果是反射則會(huì)生成DynamicExpression表達(dá)式,連接符會(huì)生成靜態(tài)的StaticExpression表達(dá)式。表達(dá)式持有了key中字符串的片段。
public static MyCacheKeyGenerator initMyKeyGenerator(Method method, String key) { Set importHashSet = new HashSet(); //根據(jù)key中的配置的方法生成表達(dá)式列表 List expressionList = new LinkedList(); generateExpression(key, expressionList); for (Expression expression : expressionList) { if (expression instanceof DynamicExpression) { String expressionStr = expression.execute(); //判斷格式合法性 String[] items = expressionStr.split("."); String indexValue = items[0].replace("args", ""); int index = Integer.parseInt(indexValue); Class clx = method.getParameterTypes()[index]; importHashSet.add(clx); //獲取對(duì)應(yīng)屬性的方法 String filedName = items[1]; String keyValue = Character.toUpperCase(filedName.charAt(0)) + filedName.substring(1); try { keyValue = "get" + keyValue; Method felidMethod = clx.getMethod(keyValue); expression.setExpression(String.format("String.valueOf(((%s)args[%s]).%s())", clx.getName(), index, felidMethod.getName())); } catch (NoSuchMethodException e) { } } } // 定義接口類型 ClassName interfaceName = ClassName.get("com.xxx.xxx", "MyKeyGenerator"); // 定義類名和包名 ClassName className = ClassName.get("com.xxx.xxx", "DyMyKeyGeneratorImpl" + classIndex.incrementAndGet()); // 創(chuàng)建類構(gòu)造器 TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className.simpleName()) .addModifiers(Modifier.PUBLIC) .addSuperinterface(interfaceName); StringBuilder stringBuilder = new StringBuilder("stringBuilder"); for (Expression expression : expressionList) { stringBuilder.append(".append(").append(expression.execute()).append(")"); } MethodSpec generateMethod = MethodSpec.methodBuilder("generate") .addModifiers(Modifier.PUBLIC) .returns(String.class) .addParameter(Object[].class, "args") .addStatement("$T stringBuilder = new StringBuilder()", StringBuilder.class) .addStatement(stringBuilder.toString()) .addStatement("return $S", "stringBuilder.toString();") .build(); classBuilder.addMethod(generateMethod); JavaFile javaFile = JavaFile.builder(className.packageName(), classBuilder.build()) .build(); StringBuilder sb = new StringBuilder(); try { javaFile.writeTo(sb); } catch (IOException e) { logger.error("寫入StringBuilder失敗", e); } try { System.out.println(sb.toString()); Map results = compiler.compile(className + ".java", sb.toString()); Class??> clazz = compiler.loadClass("com.xxx.xxx." + className, results); return (KeyGenerator) clazz.newInstance(); } catch (Exception e) { logger.error("編譯失敗,編譯內(nèi)容:{}", sb.toString(), e); throw new RuntimeException("內(nèi)存class編譯失敗"); } } public static void generateExpression(String key, List expressionList) { if (StringUtils.isEmpty(key)) { return; } int index = key.indexOf(paramsPrefix); if (index < 0) { expressionList.add(new StaticExpression(key)); return; }else{ expressionList.add(new DynamicExpression(key.substring(0, index))); } generateExpression(key.substring(index + paramsPrefix.length()), expressionList); }
生成表達(dá)式列表后開(kāi)始遍歷,最終得到key中每個(gè)arg形參與對(duì)應(yīng)的方法片段(key格式類似于@Cacheable 注解的用法。比如文章開(kāi)始時(shí)候提到的我們可以改成這樣使用,代碼如下:)
@MyCache(key="#args0.getId()"+"_"+"#args0.getName()") public Person getByIsbn(Person person) { return personMapper.getPerson(person); }
將靜態(tài)與動(dòng)態(tài)片段重新拼接放入表達(dá)式中。然后我們使用JavaPoet的接口動(dòng)態(tài)創(chuàng)建class,實(shí)現(xiàn)其中的generateKey方法,并且解析表達(dá)式填充到方法的實(shí)現(xiàn)中。最終將class加載到內(nèi)存中,再生產(chǎn)一個(gè)實(shí)例,并將這個(gè)實(shí)例緩存到內(nèi)存中。這樣下次調(diào)用就可以使用動(dòng)態(tài)生成的實(shí)例絲滑的拼接key啦!!
五. 總結(jié)
JavaPoet用法還有很多,而且@Cacheable還有很多靈活玩法,由于篇幅太長(zhǎng)就不一一呈現(xiàn)了。respect!
審核編輯 黃宇
-
代碼
+關(guān)注
關(guān)注
30文章
4823瀏覽量
68935 -
key
+關(guān)注
關(guān)注
0文章
51瀏覽量
12850
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論