ThreadLoacal缓存 - 缓存一个线程频繁调用的方法结果

适用场景:如果一次请求需要多次调用另外一个方法(该方法获取到的数据不需要修改,如调用第三方的查询接口)可以缓存该方法的结果,以便在后续线程直接使用该结果
实现原理:缓存数据存放在ThreadLocal变量中,使用aop拦截需要缓存的方法,如果缓存中有该方法的缓存结果则直接返回,如果没有则执行该方法后缓存该方法的结果。
代码如下:

1. aop注解 定义切面

 
  
  1. import java.lang.annotation.*;
  2. /**
  3. * 是否启用自定义的 线程缓存
  4. */
  5. @Documented
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target({ElementType.METHOD, ElementType.TYPE})
  8. public @interface CacheAopFitter {
  9. String value() default "";
  10. int timeout() default 10; //缓存时间(单位:秒) 默认10秒
  11. }

2. aop 拦截类 拦截注解的方法

 
  
  1. import lombok.extern.slf4j.Slf4j;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.springframework.stereotype.Component;
  7. @Aspect
  8. @Component
  9. @Slf4j
  10. public class CacheAop {
  11. //控制器切点
  12. @Pointcut("@annotation(com.mcs.cache.democache.aop.CacheAopFitter) || @within(com.mcs.cache.democache.aop.CacheAopFitter)")
  13. public void cachePoint() {
  14. }
  15. @Around("cachePoint()")
  16. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  17. //生成key
  18. String key = CacheUtils.createKey(joinPoint);
  19. //判断是否存在 如果有这直接返回
  20. if (CacheUtils.containsKey(key)) {
  21. return CacheUtils.getByKey(key);
  22. }
  23. Object result = joinPoint.proceed();
  24. //缓存结果
  25. CacheUtils.setValueByKey(key, result);
  26. //返回结果
  27. return result;
  28. }
  29. }

3. 相应的工具类用户设置或者获取缓存

 
  
  1. import com.alibaba.fastjson.JSONObject;
  2. import lombok.Data;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.reflect.MethodSignature;
  6. import org.springframework.beans.BeanUtils;
  7. import java.math.BigInteger;
  8. import java.security.MessageDigest;
  9. import java.security.NoSuchAlgorithmException;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. @Slf4j
  13. public class CacheUtils {
  14. //缓存对象 这里是线程缓存需要设置成静态 同一个线程内共享,不同线程不共享
  15. //实现原理看 Threadlocal机制
  16. static ThreadLocal localStorage = ThreadLocal.withInitial(() -> new MyCahce());
  17. public static MyCahce get() {
  18. return localStorage.get();
  19. }
  20. public static void set(MyCahce myCahce) {
  21. localStorage.set(myCahce);
  22. }
  23. /**
  24. * 移除本地变量
  25. */
  26. public static void remove() {
  27. localStorage.remove();
  28. }
  29. /**
  30. * 判断是否包含key
  31. *
  32. * @param key
  33. * @return
  34. */
  35. public static Boolean containsKey(String key) {
  36. MyCahce myCahce = getMyCahce();
  37. log.debug(JSONObject.toJSONString(myCahce));
  38. return myCahce.getMap().containsKey(key);
  39. }
  40. /**
  41. * 根据key 获取缓存的值
  42. *
  43. * @param key
  44. * @return
  45. */
  46. public static Object getByKey(String key) {
  47. MyCahce myCahce = getMyCahce();
  48. if (myCahce.getMap().containsKey(key)) {
  49. return myCahce.getMap().get(key);
  50. }
  51. return null;
  52. }
  53. /**
  54. * 设置缓存的值
  55. *
  56. * @param key
  57. * @param value
  58. */
  59. public static void setValueByKey(String key, Object value) {
  60. MyCahce myCahce = getMyCahce();
  61. myCahce.getMap().put(key, value);
  62. }
  63. /**
  64. * 获取缓存map
  65. * @return
  66. */
  67. private static MyCahce getMyCahce() {
  68. MyCahce myCahce = localStorage.get();
  69. if (myCahce == null) {
  70. myCahce = new MyCahce();
  71. localStorage.set(myCahce);
  72. }
  73. Map map = myCahce.getMap();
  74. String key = "startTime";
  75. if (map.containsKey(key)) {
  76. //超时时间
  77. int timeout = myCahce.getMyAnnotation().getTimeout();
  78. long startTime = (long) map.get(key);
  79. //如果大于超时时间 则清空缓存
  80. if (System.currentTimeMillis() - startTime > timeout * 1000) {
  81. map.clear();
  82. }
  83. } else {
  84. map.put(key, System.currentTimeMillis());
  85. }
  86. return myCahce;
  87. }
  88. /**
  89. * 生成 key
  90. *
  91. * @param joinPoint
  92. * @return
  93. */
  94. static String createKey(ProceedingJoinPoint joinPoint) {
  95. //类名
  96. String className = joinPoint.getTarget().getClass().getName();
  97. //方法名
  98. String methodName = joinPoint.getSignature().getName();
  99. //参数对象
  100. Object[] methodArgs = joinPoint.getArgs();
  101. String key = className + "." + methodName + Thread.currentThread().getId() + "|" + MD5(JSONObject.toJSONString(methodArgs));
  102. //初始化注解参数
  103. initAnnotation(joinPoint);
  104. return key;
  105. }
  106. /**
  107. * 初始化注解参数
  108. * @param joinPoint
  109. * @return
  110. */
  111. static void initAnnotation(ProceedingJoinPoint joinPoint) {
  112. MyAnnotation myAnnotation = getAnnotation(joinPoint);
  113. if (myAnnotation != null) {
  114. MyCahce myCahce = getMyCahce();
  115. MyAnnotation myAnnotation1 = myCahce.getMyAnnotation();
  116. BeanUtils.copyProperties(myAnnotation, myAnnotation1);
  117. }
  118. }
  119. /**
  120. * md5 方法
  121. *
  122. * @param plainText
  123. * @return
  124. */
  125. private static String MD5(String plainText) {
  126. //定义一个字节数组
  127. byte[] secretBytes = null;
  128. try {
  129. // 生成一个MD5加密计算摘要
  130. MessageDigest md = MessageDigest.getInstance("MD5");
  131. //对字符串进行加密
  132. md.update(plainText.getBytes());
  133. //获得加密后的数据
  134. secretBytes = md.digest();
  135. } catch (NoSuchAlgorithmException e) {
  136. throw new RuntimeException("没有md5这个算法!");
  137. }
  138. //将加密后的数据转换为16进制数字
  139. String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
  140. // 如果生成数字未满32位,需要前面补0
  141. for (int i = 0; i < 32 - md5code.length(); i++) {
  142. md5code = "0" + md5code;
  143. }
  144. return md5code;
  145. }
  146. /**
  147. * 获取注解中的参数
  148. *
  149. * @param joinPoint
  150. * @return
  151. */
  152. private static CacheUtils.MyAnnotation getAnnotation(ProceedingJoinPoint joinPoint) {
  153. CacheUtils.MyAnnotation myAnnotation = new CacheUtils.MyAnnotation();
  154. CacheAopFitter cacheAopFitter = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CacheAopFitter.class);
  155. if (cacheAopFitter == null) {
  156. cacheAopFitter = joinPoint.getTarget().getClass().getAnnotation(CacheAopFitter.class);
  157. }
  158. if (cacheAopFitter != null) {
  159. myAnnotation.setTimeout(cacheAopFitter.timeout());
  160. myAnnotation.setValue(cacheAopFitter.value());
  161. }
  162. return myAnnotation;
  163. }
  164. /**
  165. * 缓存类 主要是存放缓存的数据 以及注解信息
  166. */
  167. @Data
  168. public static class MyCahce {
  169. private Map map = new HashMap<>();
  170. private MyAnnotation myAnnotation = new MyAnnotation();
  171. }
  172. /**
  173. * 注解信息类
  174. */
  175. @Data
  176. public static class MyAnnotation {
  177. String value = "";
  178. int timeout = 10;
  179. }
  180. }

你可能感兴趣的:(缓存,java,开发语言)