/** Copyright 2009 by primeton Corporation. * * All rights reserved. * * This software is the confidential and proprietary information of * primeton Corporation ('Confidential Information'). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with primeton. */ package com.primeton.dgs.kernel.core.cache.impl; import java.lang.reflect.Method; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import com.primeton.dgs.kernel.core.cache.CacheEvict; import com.primeton.dgs.kernel.core.cache.Cacheable; import com.primeton.dgs.kernel.core.util.StringFullUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.aspectj.lang.ProceedingJoinPoint; import com.primeton.dgs.kernel.core.cache.ICache; import com.primeton.dgs.kernel.core.cache.ICacheInterceptor; import com.primeton.dgs.kernel.core.cache.ICacheKeyCreator; import org.aspectj.lang.Signature; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author xiongbin * @author zhaopx 缓存拦截器 * @Version 2012-8-28 */ public class CacheInterceptorImpl implements ICacheInterceptor { private static final Logger log = LoggerFactory.getLogger(CacheInterceptorImpl.class); /** * 缓存 */ private ICache cache; /** * 缓存的 Key 生成逻辑管理器 */ private DefaultCacheKeyCreatorManager manager; public Object add(ProceedingJoinPoint jp){ try { Object[] args = jp.getArgs(); // 通过 AOP 拦截的类全名,获得该类的 ICacheKeyCreator,缓存 ID 生成器 // ICacheKeyCreator 一定是非空的 ICacheKeyCreator creator = manager.getCacheKeyCreator(jp.getTarget().getClass().getName()); // 获取到该方法需要缓存的 Name String methodName = jp.getSignature().getName(); // 通过参数生成缓存 ID String key = getKey(creator.getCacheKey(args)); if(StringUtils.isNotBlank(key)){ final String cacheKey = methodName + "_" + key; synchronized (this) { Object result = cache.get(cacheKey); if(result == null){ result = jp.proceed(); // 调用取得返回值缓存,如果是从缓存中取出,无须继续放入缓存,减少 cache.add(cacheKey, creator.getExpTime(methodName), result); } return result; } } // 无参数函数无法缓存,或者缓存的 Key 无法生成,不缓存 return jp.proceed(); } catch (Throwable e) { log.error("AOP 调用接口异常。", e); throw new IllegalStateException(e); } } /** * 根据 接口的注解支持缓存 * * add zhaopx at: 2019/09/24 * @param jp * @return */ public Object cachedAnno(ProceedingJoinPoint jp){ try { Object[] args = jp.getArgs(); Class aClass = jp.getTarget().getClass(); Signature signature = jp.getSignature(); String methodName = signature.getName(); MethodSignature msig = null; if (!(signature instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用于方法"); } msig = (MethodSignature) signature; Class[] parameterTypes = msig.getParameterTypes(); Method cachedMethod = aClass.getMethod(msig.getName(), parameterTypes); // 方法参数,构成一个 Map,用于拼接 Cache key final Map argsMap = new HashMap<>(parameterTypes.length); argsMap.put("methodName", methodName); argsMap.put("className", aClass.getSimpleName()); for (int i = 0; i < args.length; i++) { String argValue = (args[i] == null ? "" : String.valueOf(args[i])); argsMap.put("arg" + i, argValue); } // JDK8 才执行 if(SystemUtils.IS_JAVA_1_8) { int i = 0; // 这样的方式导入,否则 JDK7 一下报错 java.lang.reflect.Parameter[] parameters = cachedMethod.getParameters(); for (java.lang.reflect.Parameter parameter : parameters) { String argValue = (args[i] == null ? "" : String.valueOf(args[i])); argsMap.put(parameter.getName(), argValue); i++; } } // 删除缓存的策略 CacheEvict cacheEvictAnno = cachedMethod.getAnnotation(CacheEvict.class); if(cacheEvictAnno != null) { if(cacheEvictAnno.allEntries()) { // 删除所有缓存键 cache.clear(); } else { String[] keys = cacheEvictAnno.keys(); for (String key : keys) { // 依次删除所有缓存 Key // 宏替换,替换 ${} 内的变量 key = StringFullUtils.getFullString(key, argsMap); cache.remove(key); log.warn("remove cache key {}", key); } // 前缀删除,当前还看怎么支持 if(StringUtils.isNotBlank(cacheEvictAnno.keyPrefix())) { try { cache.removeByPrefix(cacheEvictAnno.keyPrefix()); } catch (Exception e) {} } } } // 获取方法注解 Cacheable cachedAnno = cachedMethod.getAnnotation(Cacheable.class); if(cachedAnno == null) { // 接口上没有 Cacheable 的注解,不进行缓存 // log.info("execute no cache method {}.{}", aClass.getName(), methodName); // 无参数函数无法缓存,或者缓存的 Key 无法生成,不缓存 return jp.proceed(); } else { final long start = System.currentTimeMillis(); String key = cachedAnno.key(); // 宏替换,替换 ${} 内的变量 key = StringFullUtils.getFullString(key, argsMap); Object result = cache.get(key); if(result == null){ log.info("no cache key {}, execute method {}.{}", key, aClass.getName(), methodName); result = jp.proceed(); if(cachedAnno.expire() > 0) { // 有缓存过期时间的 cache.add(key, cachedAnno.expire(), result); } else { cache.add(key, result); } } else { log.info("cached key {}, return result from cache(exe {}, cost time {} ms).", key, methodName, (System.currentTimeMillis() - start)); } return result; } } catch (Throwable e) { log.error("AOP 调用接口异常。", e); throw new IllegalStateException(e.getMessage()); } } private String getKey(String key) { if(key.matches("[*:]")){ return ""; } System.out.println("memcachedKEY:"+key); try { key = encryptMD5(key); } catch (Exception e) { log.error("生成 md5 异常。", e); } return key; } public void refresh() { try { cache.clear(); } catch (Exception e) { log.error("清除缓存异常。", e); } } public DefaultCacheKeyCreatorManager getManager() { return manager; } public void setManager(DefaultCacheKeyCreatorManager manager) { this.manager = manager; } public ICache getCache() { return cache; } public void setCache(ICache cache) { this.cache = cache; } /** * 使用MD5加密 * @param originalText 明文 * @return 密文 * @throws Exception JDK不支持MD5加密算法 */ private String encryptMD5(String originalText) throws Exception { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(originalText.getBytes()); byte[] digest = md5.digest(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < digest.length; i++) { String s = Integer.toHexString(digest[i] & 0XFF); if (s.length() == 1) { sb.append(i).append(s); } else { sb.append(s); } if (i < digest.length-1) { sb.append(i); } } return sb.toString().toUpperCase(); } }