基于SpringAOP+Caffeine實現(xiàn)本地緩存的實例代碼
基于SpringAOP+Caffeine實現(xiàn)本地緩存的實例,文中有詳細的代碼供大家參考,需要的朋友可以參考下
一、背景
公司想對一些不經(jīng)常變動的數(shù)據(jù)做一些本地緩存,我們使用AOP+Caffeine來實現(xiàn)
二、實現(xiàn)
1、定義注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 本地緩存 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LocalCacheable { // 過期時間 默認(rèn)10分鐘 long expired() default 600; // key創(chuàng)建器 String keyGenerator() default "org.springframework.cache.interceptor.KeyGenerator"; }
2、切面
import com.google.gson.internal.LinkedTreeMap; import org.apache.commons.lang3.ArrayUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.support.AopUtils; import org.springframework.cglib.proxy.Enhancer; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 本地緩存 */ @Aspect @Component public class LocalCacheAspect { private static final String separator = ":"; @Around("@annotation(com.framework.localcache.LocalCacheable)") public Object around(ProceedingJoinPoint point) throws Throwable { if (AopUtils.isAopProxy(point.getTarget())) { return point.proceed(); } Method method = getMethodSignature(point).getMethod(); if (method == null) { return point.proceed(); } LocalCacheable annotation = method.getAnnotation(LocalCacheable.class); if (annotation == null) { return point.proceed(); } // 生成key String key = generateKey(point); // System.out.println("生成的key:" + key); long expired = annotation.expired(); Throwable[] throwable = new Throwable[1]; Object proceed = LocalCache.cacheData(key, () -> { try { return point.proceed(); } catch (Throwable e) { throwable[0] = e; } return null; }, expired); if (throwable[0] != null) { throw throwable[0]; } return proceed; } /** * 獲取方法 */ private MethodSignature getMethodSignature(ProceedingJoinPoint point) { Signature signature = point.getSignature(); if (signature instanceof MethodSignature) { return ((MethodSignature) signature); } return null; } /** * 獲取key */ private String generateKey(ProceedingJoinPoint point) { // 目標(biāo)類、方法、參數(shù)等 Class<?> targetClass = AopProxyUtils.ultimateTargetClass(point.getTarget()); Method method = getMethodSignature(point).getMethod(); String[] parameterNames = getMethodSignature(point).getParameterNames(); Object[] args = point.getArgs(); // 解析參數(shù),生成key LinkedTreeMap<String, Object> paramResolveResult = new LinkedTreeMap<>(); if (ArrayUtils.isNotEmpty(args)) { for (int i = 0; i < args.length; i++) { resolveParam(args[i], paramResolveResult, parameterNames[i]); } } StringBuilder key = new StringBuilder(targetClass.getName() + separator + method.getName() + separator); paramResolveResult.forEach((k, v) -> { if (v != null) { key.append(k + "," + v + separator); } }); // 根據(jù)方法名和參數(shù)生成唯一標(biāo)識 return key.toString(); } private void resolveParam(Object param, Map<String, Object> paramResolveResult, String prefix) { if (param == null) { return; } Class<?> type = param.getClass(); if (type == List.class) { List<Object> param0 = (List) param; for (int i = 0; i < param0.size(); i++) { resolveParam(param0.get(i), paramResolveResult, prefix + "[" + i + "]"); } } else if (type == Map.class) { Map<Object, Object> param0 = (Map) param; param0.forEach((k, v) -> { resolveParam(v, paramResolveResult, prefix + "." + k); }); } else if (type.isArray()) { Object[] param0 = (Object[]) param; for (int i = 0; i < param0.length; i++) { resolveParam(param0[i], paramResolveResult, prefix + "[" + i + "]"); } } else if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class || type == Float.class || type == Double.class || type == Boolean.class || type == Character.class || type == String.class) { paramResolveResult.put(prefix, param); } else { // 復(fù)雜類型 Map<String, Object> fieldMap = new HashMap<>(); // CGLIB代理 if (Enhancer.isEnhanced(type)) { getAllFieldsAndValue(param, type.getSuperclass(), fieldMap); } else { getAllFieldsAndValue(param, type, fieldMap); } fieldMap.forEach((k, v) -> { if (v == null) { return; } resolveParam(v, paramResolveResult, prefix + "." + k); }); } } /** * 獲取所有字段和值 */ private void getAllFieldsAndValue(Object o, Class type, Map<String, Object> fieldMap) { for (Field field : type.getDeclaredFields()) { if (field.trySetAccessible()) { try { fieldMap.put(field.getName(), field.get(o)); } catch (IllegalAccessException e) {} } } if (type.getSuperclass() != Object.class) { getAllFieldsAndValue(o, type.getSuperclass(), fieldMap); } } }
3、緩存工具類
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** * 本地緩存 */ public class LocalCache { private static final Map<Long, Cache<Object, Object>> cacheMap = new ConcurrentHashMap<>(); /** * 創(chuàng)建本地緩存 * @param seconds 過期時間:秒 */ private static Cache<Object, Object> createCache(long seconds) { return Caffeine.newBuilder() .expireAfterWrite(seconds, TimeUnit.SECONDS) .build(); } /** * 創(chuàng)建本地緩存 * @param seconds 過期時間:秒 * @param loader 緩存方法 */ private Cache<Object, Object> createLoadingCache(long seconds, CacheLoader<Object, Object> loader) { return Caffeine.newBuilder() .expireAfterWrite(seconds, TimeUnit.SECONDS) .build(loader); } /** * 獲取一個緩存組 * @param seconds 緩存過期時間 */ private static Cache<Object, Object> getAndLoad(long seconds) { if (cacheMap.containsKey(seconds)) { return cacheMap.get(seconds); } Cache<Object, Object> cache = createCache(seconds); cacheMap.put(seconds, cache); return cache; } /** * 緩存數(shù)據(jù),過期時間默認(rèn)10分鐘 * @param key key * @param supplier 數(shù)據(jù)來源的方法 */ public static Object cacheData(Object key, Supplier<Object> supplier) { return cacheData(key, supplier, 600); } /** * 緩存數(shù)據(jù) * @param key key * @param supplier 數(shù)據(jù)來源的方法 * @param seconds 過期時間:秒 */ public static Object cacheData(Object key, Supplier<Object> supplier, long seconds) { Assert.state(seconds > 0, "過期時間必須大于0秒"); Cache<Object, Object> cache = getAndLoad(seconds); return cache.get(key, k -> supplier.get()); } }
三、測試
@LocalCacheable @GetMapping("test1") public String test1() { System.out.println("執(zhí)行了"); return "success"; } @LocalCacheable @GetMapping("test2") public String test2(String a) { System.out.println("執(zhí)行了" + a); return "success"; } @LocalCacheable @GetMapping("test3") public String test3(String a, int b, String c) { System.out.println("執(zhí)行了" + a + b + c); return "success"; } @LocalCacheable @GetMapping("test4") public String test4(UserInfo user) { System.out.println("執(zhí)行了" + user); return "success"; } @LocalCacheable @GetMapping("test5") public String test5(UserInfo[] users) { System.out.println("執(zhí)行了" + users); return "success"; } @LocalCacheable @GetMapping("test6") public String test6(List<UserInfo> users) { System.out.println("執(zhí)行了" + users); return "success"; } @LocalCacheable @GetMapping("test7") public String test7(UserInfo user) { System.out.println("執(zhí)行了" + user.getMap()); return "success"; }
到此這篇關(guān)于基于SpringAOP+Caffeine實現(xiàn)本地緩存的文章就介紹到這了,更多相關(guān)SpringAOP affeine本地緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入探究Bean生命周期的擴展點Bean Post Processor
在Spring框架中,Bean生命周期的管理是非常重要的一部分,在Bean的創(chuàng)建、初始化和銷毀過程中,Spring提供了一系列的擴展點,其中,Bean Post Processor(后處理器)是一個重要的擴展點,它能夠在Bean的初始化前后做一些額外的處理,本文就和大家一起深入探究2023-07-07IDEA啟動Springboot報錯:無效的目標(biāo)發(fā)行版:17 的解決辦法
這篇文章主要給大家介紹了IDEA啟動Springboot報錯:無效的目標(biāo)發(fā)行版:17 的解決辦法,文中通過代碼示例和圖文講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02SpringBoot中攔截器和動態(tài)代理的區(qū)別詳解
在?Spring?Boot?中,攔截器和動態(tài)代理都是用來實現(xiàn)功能增強的,所以在很多時候,有人會認(rèn)為攔截器的底層是通過動態(tài)代理實現(xiàn)的,所以本文就來盤點一下他們兩的區(qū)別,以及攔截器的底層實現(xiàn)吧2023-09-09java 漢諾塔Hanoi遞歸、非遞歸(仿系統(tǒng)遞歸)和非遞歸規(guī)律 實現(xiàn)代碼
漢諾塔(Hanoi) 算法Java實現(xiàn)。通過三個函數(shù),分別對Hanoi進行遞歸、非遞歸和非遞歸規(guī)律實現(xiàn)。2013-05-05