aop缓存,支持threadlocal、memory、redis

泛型反序列化

 
  1. /**
  2. * 字符串转换成对象 支持多级转换以及泛型等 比如 UCRoot<List<Hospital>> 、List<Hospital> 等
  3. * eg:
  4. *
  5. * @param jsonStr
  6. * @param types
  7. * @return
  8. */
  9. public static Object parseObject(String jsonStr, Type[] types) {
  10. if (isBlank(jsonStr) || ArrayUtils.isEmpty(types)) {
  11. return null;
  12. }
  13. if (types.length == 1) {
  14. if (jsonStr.startsWith("[{")) {
  15. return JSONObject.parseArray(jsonStr, (Class) types[0]);
  16. } else {
  17. return JSONObject.parseObject(jsonStr, types[0]);
  18. }
  19. }
  20. ParameterizedTypeImpl beforeType = null;
  21. if (types != null && types.length > 0) {
  22. for (int i = types.length - 1; i > 0; i--) {
  23. beforeType = new ParameterizedTypeImpl(new Type[]{beforeType == null ? types[i] : beforeType}, null, types[i - 1]);
  24. }
  25. }
  26. return JSONObject.parseObject(jsonStr, beforeType);
  27. }

1.注解

 
  1. import java.lang.annotation.*;
  2. /**
  3. * 是否启用自定义的 线程缓存
  4. *
  5. */
  6. @Documented
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target({ElementType.METHOD, ElementType.TYPE})
  9. public @interface CacheAopFitter {
  10. String value() default "";
  11. int timeout() default 15; //缓存时间(单位:秒)
  12. /**
  13. * 缓存类型 1:线程缓存 2:内存缓存 3:Redis缓存
  14. *
  15. * @return
  16. */
  17. CacheUtils.CacheType cacheType() default CacheUtils.CacheType.Redis;
  18. /**
  19. * list 或者泛型 如果是 List 可以省略掉 List.class 反序列化时会判断是否是list类型
  20. * eg UCRoot<List<Pharmacy>>:[UCRoot.class,List.class,Pharmacy.class] 、 List<Pharmacy>:[List.class,Pharmacy.class]
  21. *
  22. * @return
  23. */
  24. Class[] classes() default {};
  25. }

2 aop拦截器

 
  1. import cn.ucmed.baseline.d2d.service.Cache.RedisService;
  2. import com.ucmed.common.util.CommonUtil;
  3. import lombok.extern.log4j.Log4j;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10. /**
  11. * AOP 对静态的方法没用
  12. * 药事获取用户中心的数据都是 使用静态方法(aop没有生效) 暂时不修改
  13. */
  14. @Aspect
  15. @Component
  16. @Log4j
  17. public class CacheAop {
  18. @Autowired
  19. RedisService redisService;
  20. //控制器切点
  21. @Pointcut("@annotation(cn.ucmed.baseline.d2d.aop.CacheAopFitter) || @within(cn.ucmed.baseline.d2d.aop.CacheAopFitter)")
  22. public void cachePoint() {
  23. }
  24. @Around("cachePoint()")
  25. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  26. //获取注解参数
  27. CacheUtils.MyAnnotation myAnnotation = CacheUtils.getAnnotation(joinPoint);
  28. Object object = null;
  29. //生成key
  30. String key = "";
  31. if (myAnnotation.cacheType == CacheUtils.CacheType.ThreadLocal) {
  32. key = CacheUtils.createKey(joinPoint);
  33. //获取缓存的值
  34. object = CacheUtils.getByKey(key);
  35. } else if (myAnnotation.cacheType == CacheUtils.CacheType.Memory) {
  36. key = CacheUtils.createKeyForMemory(joinPoint);
  37. //获取缓存的值
  38. object = CacheUtils.getByKeyForMemory(key);
  39. } else if (myAnnotation.cacheType == CacheUtils.CacheType.Redis) {
  40. key = CacheUtils.createKeyForRedis(joinPoint);
  41. //获取缓存的值
  42. object = redisService.getObject(key, myAnnotation.getClasses());
  43. }
  44. //判断是否存在 如果有这直接返回
  45. if (object != null) return object;
  46. Object value = joinPoint.proceed();
  47. //缓存结果
  48. if (value != null) {
  49. if (myAnnotation.cacheType == CacheUtils.CacheType.ThreadLocal) {
  50. CacheUtils.setValueByKey(key, value, myAnnotation);
  51. } else if (myAnnotation.cacheType == CacheUtils.CacheType.Memory) {
  52. CacheUtils.setValueByKeyForMemory(key, value, myAnnotation);
  53. } else if (myAnnotation.cacheType == CacheUtils.CacheType.Redis) {
  54. redisService.setObject(key, value, myAnnotation.timeout);
  55. }
  56. }
  57. //返回结果
  58. return value;
  59. }
  60. }

3工具类

 
  1. import com.alibaba.fastjson.JSONObject;
  2. import lombok.Data;
  3. import lombok.extern.log4j.Log4j;
  4. import org.apache.commons.lang.ArrayUtils;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.reflect.MethodSignature;
  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. import java.util.concurrent.ConcurrentHashMap;
  13. /**
  14. * @Description
  15. * @auther machunsen
  16. * @create 2019-07-22 15:31:12
  17. */
  18. @Log4j
  19. public class CacheUtils {
  20. //线程缓存
  21. private static ThreadLocal<Map<String, MyValue>> localStorage = ThreadLocal.withInitial(HashMap::new);
  22. //内存缓存
  23. private static Map<String, MyValue> memoryCache = new ConcurrentHashMap<>();
  24. //上一次清理时间
  25. private static String cacheLastClearTime = "YSFWPT_cacheLastClearTime";
  26. //清理缓存间隔
  27. private static Long clearCacheInterval = 30 * 60 * 1000L;
  28. //region ThreadLocal缓存
  29. public static Map<String, MyValue> get() {
  30. return localStorage.get();
  31. }
  32. public static void set(Map<String, MyValue> map) {
  33. localStorage.set(map);
  34. }
  35. /**
  36. * 移除本地变量
  37. */
  38. public static void remove() {
  39. localStorage.remove();
  40. }
  41. /**
  42. * 根据key 获取缓存的值
  43. *
  44. * @param key
  45. * @return
  46. */
  47. public static Object getByKey(String key) {
  48. Map<String, MyValue> map = getMyCache();
  49. return getValueByKey(map, key);
  50. }
  51. /**
  52. * 设置缓存的值
  53. *
  54. * @param key
  55. * @param value
  56. */
  57. public static void setValueByKey(String key, Object value, MyAnnotation myAnnotation) {
  58. Map<String, MyValue> map = getMyCache();
  59. setValue(map, key, value, myAnnotation);
  60. }
  61. /**
  62. * 获取缓存map
  63. *
  64. * @return
  65. */
  66. private static Map<String, MyValue> getMyCache() {
  67. Map<String, MyValue> map = localStorage.get();
  68. if (map == null) {
  69. localStorage.set(new HashMap<>());
  70. }
  71. return map;
  72. }
  73. //endregion
  74. //region 内存缓存
  75. public static Object getByKeyForMemory(String key) {
  76. return getValueByKey(memoryCache, key);
  77. }
  78. public static void setValueByKeyForMemory(String key, Object value, MyAnnotation myAnnotation) {
  79. setValue(memoryCache, key, value, myAnnotation);
  80. }
  81. //endregion
  82. private static Object getValueByKey(Map<String, MyValue> map, String key) {
  83. if (map == null) return null;
  84. //判断是否需要清空缓存
  85. MyValue myValue = map.get(cacheLastClearTime);
  86. boolean needInitStartTime = false;
  87. if (myValue != null) {
  88. //如果大于清除间隔则清除缓存
  89. if (System.currentTimeMillis() - myValue.startTime > clearCacheInterval) {
  90. map.clear();
  91. return null;
  92. }
  93. } else {
  94. needInitStartTime = true;
  95. }
  96. //出事化清除间隔的开始时间
  97. if (needInitStartTime) {
  98. map.put(cacheLastClearTime, new MyValue());
  99. }
  100. //获取值
  101. myValue = map.get(key);
  102. if (myValue != null) {
  103. //如果超时了则移除
  104. if (System.currentTimeMillis() - myValue.startTime > myValue.getMyAnnotation().getTimeout() * 1000) {
  105. map.remove(key);
  106. } else {
  107. return myValue.getValue();
  108. }
  109. }
  110. return null;
  111. }
  112. private static void setValue(Map<String, MyValue> map, String key, Object value, MyAnnotation myAnnotation) {
  113. if (map == null) return;
  114. MyValue myValue = new MyValue();
  115. myValue.setValue(value);
  116. myValue.setMyAnnotation(myAnnotation);
  117. map.put(key, myValue);
  118. }
  119. /**
  120. * 生成 key
  121. *
  122. * @param joinPoint
  123. * @return
  124. */
  125. static String createKey(ProceedingJoinPoint joinPoint) {
  126. //类名
  127. String className = joinPoint.getTarget().getClass().getName();
  128. //方法名
  129. String methodName = joinPoint.getSignature().getName();
  130. //参数对象
  131. Object[] methodArgs = joinPoint.getArgs();
  132. String key = className + "." + methodName + Thread.currentThread().getId() + MD5(JSONObject.toJSONString(methodArgs));
  133. return key;
  134. }
  135. static String createKeyForMemory(ProceedingJoinPoint joinPoint) {
  136. //类名
  137. String className = joinPoint.getTarget().getClass().getName();
  138. //方法名
  139. String methodName = joinPoint.getSignature().getName();
  140. //参数对象
  141. Object[] methodArgs = joinPoint.getArgs();
  142. String key = className + "." + methodName + MD5(JSONObject.toJSONString(methodArgs));
  143. return key;
  144. }
  145. static String createKeyForRedis(ProceedingJoinPoint joinPoint) {
  146. //类名
  147. String className = joinPoint.getTarget().getClass().getName();
  148. //方法名
  149. String methodName = joinPoint.getSignature().getName();
  150. //参数对象
  151. Object[] methodArgs = joinPoint.getArgs();
  152. String key = className + "." + methodName + JSONObject.toJSONString(methodArgs);
  153. key = methodName + "_" + MD5(key);
  154. return key;
  155. }
  156. /**
  157. * md5 方法
  158. *
  159. * @param plainText
  160. * @return
  161. */
  162. private static String MD5(String plainText) {
  163. //定义一个字节数组
  164. byte[] secretBytes = null;
  165. try {
  166. // 生成一个MD5加密计算摘要
  167. MessageDigest md = MessageDigest.getInstance("MD5");
  168. //对字符串进行加密
  169. md.update(plainText.getBytes());
  170. //获得加密后的数据
  171. secretBytes = md.digest();
  172. } catch (NoSuchAlgorithmException e) {
  173. throw new RuntimeException("没有md5这个算法!");
  174. }
  175. //将加密后的数据转换为16进制数字
  176. String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
  177. // 如果生成数字未满32位,需要前面补0
  178. for (int i = 0; i < 32 - md5code.length(); i++) {
  179. md5code = "0" + md5code;
  180. }
  181. return md5code;
  182. }
  183. /**
  184. * 获取注解中的参数
  185. *
  186. * @param joinPoint
  187. * @return
  188. */
  189. public static CacheUtils.MyAnnotation getAnnotation(ProceedingJoinPoint joinPoint) {
  190. CacheUtils.MyAnnotation myAnnotation = new CacheUtils.MyAnnotation();
  191. CacheAopFitter cacheAopFitter = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CacheAopFitter.class);
  192. if (cacheAopFitter == null) {
  193. cacheAopFitter = joinPoint.getTarget().getClass().getAnnotation(CacheAopFitter.class);
  194. }
  195. if (cacheAopFitter != null) {
  196. myAnnotation.setTimeout(cacheAopFitter.timeout());
  197. myAnnotation.setValue(cacheAopFitter.value());
  198. myAnnotation.setCacheType(cacheAopFitter.cacheType());
  199. if (!ArrayUtils.isEmpty(cacheAopFitter.classes())) {
  200. myAnnotation.setClasses(cacheAopFitter.classes());
  201. } else {
  202. try {
  203. Class classzz = Class.forName(joinPoint.getSignature().toLongString().split(" ")[1]);
  204. myAnnotation.setClasses(new Class[]{classzz});
  205. } catch (Exception ex) {
  206. log.error("缓存转换类型失败", ex);
  207. }
  208. }
  209. }
  210. return myAnnotation;
  211. }
  212. // /**
  213. // * 缓存类
  214. // */
  215. // @Data
  216. // public static class MyCahce {
  217. //
  218. // private Map<String, MyValue> map = new HashMap<>();
  219. // private MyAnnotation myAnnotation = new MyAnnotation();
  220. // }
  221. /**
  222. * 值对象
  223. */
  224. @Data
  225. public static class MyValue {
  226. private Object value;
  227. private Long startTime = System.currentTimeMillis();
  228. MyAnnotation myAnnotation;
  229. }
  230. /**
  231. * 缓存的类型 线程缓存和内存缓存不适合缓存比较大的数据
  232. * 比较大的数据可以使用redis缓存
  233. */
  234. public static enum CacheType {
  235. /**
  236. * 线程缓存
  237. */
  238. ThreadLocal,
  239. /**
  240. * 直接缓存在内存中 ConcurrentHashMap中
  241. */
  242. Memory,
  243. /**
  244. * //使用redis 如果返回值是泛型 使用redis不能反序列化,导致获取失败
  245. */
  246. Redis;
  247. }
  248. /**
  249. * 缓存类
  250. */
  251. @Data
  252. public static class MyAnnotation {
  253. String value = "";
  254. int timeout = 15;//超时时间
  255. CacheType cacheType = CacheType.Redis; //缓存类型
  256. //返回值类型
  257. Class[] classes;
  258. }
  259. }

[转载]fastjson反序列化多层嵌套泛型类与java中的Type类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值