作者:京東物流 方志民
一. 背景
在一次系統(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;
}
}
書(shū)接上文,獲取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)寫(xiě)直接展示生成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("寫(xiě)入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文章
4940瀏覽量
73076 -
key
+關(guān)注
關(guān)注
0文章
53瀏覽量
13253
發(fā)布評(píng)論請(qǐng)先 登錄
LRU緩存模塊最佳實(shí)踐
本地緩存的技術(shù)實(shí)踐
緩存有大key?你得知道的一些手段
【RTC程序設(shè)計(jì):實(shí)時(shí)音視頻權(quán)威指南】傳輸控制優(yōu)化閱讀 及其實(shí)踐分享
基于Java的分布式緩存優(yōu)化在網(wǎng)絡(luò)管理系統(tǒng)中的應(yīng)用
由緩存和優(yōu)化引起的DMA問(wèn)題
面向多目標(biāo)優(yōu)化的自適應(yīng)SSD緩存系統(tǒng)
費(fèi)用優(yōu)化的云存儲(chǔ)緩存策略
緩存服務(wù)器運(yùn)作的原理解析
如何設(shè)計(jì)一個(gè)緩存系統(tǒng)?
緩存被穿透了如何解決
高性能緩存設(shè)計(jì):如何解決緩存偽共享問(wèn)題

基于javaPoet的緩存key優(yōu)化實(shí)踐
評(píng)論