基于SpringAOP+Caffeine實(shí)現(xiàn)本地緩存的實(shí)例代碼
基于SpringAOP+Caffeine實(shí)現(xiàn)本地緩存的實(shí)例,文中有詳細(xì)的代碼供大家參考,需要的朋友可以參考下
一、背景
公司想對(duì)一些不經(jīng)常變動(dòng)的數(shù)據(jù)做一些本地緩存,我們使用AOP+Caffeine來(lái)實(shí)現(xiàn)
二、實(shí)現(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 { // 過(guò)期時(shí)間 默認(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)識(shí) 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 過(guò)期時(shí)間:秒 */ private static Cache<Object, Object> createCache(long seconds) { return Caffeine.newBuilder() .expireAfterWrite(seconds, TimeUnit.SECONDS) .build(); } /** * 創(chuàng)建本地緩存 * @param seconds 過(guò)期時(shí)間:秒 * @param loader 緩存方法 */ private Cache<Object, Object> createLoadingCache(long seconds, CacheLoader<Object, Object> loader) { return Caffeine.newBuilder() .expireAfterWrite(seconds, TimeUnit.SECONDS) .build(loader); } /** * 獲取一個(gè)緩存組 * @param seconds 緩存過(guò)期時(shí)間 */ 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ù),過(guò)期時(shí)間默認(rèn)10分鐘 * @param key key * @param supplier 數(shù)據(jù)來(lái)源的方法 */ public static Object cacheData(Object key, Supplier<Object> supplier) { return cacheData(key, supplier, 600); } /** * 緩存數(shù)據(jù) * @param key key * @param supplier 數(shù)據(jù)來(lái)源的方法 * @param seconds 過(guò)期時(shí)間:秒 */ public static Object cacheData(Object key, Supplier<Object> supplier, long seconds) { Assert.state(seconds > 0, "過(guò)期時(shí)間必須大于0秒"); Cache<Object, Object> cache = getAndLoad(seconds); return cache.get(key, k -> supplier.get()); } }
三、測(cè)試
@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實(shí)現(xiàn)本地緩存的文章就介紹到這了,更多相關(guān)SpringAOP affeine本地緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入探究Bean生命周期的擴(kuò)展點(diǎn)Bean Post Processor
在Spring框架中,Bean生命周期的管理是非常重要的一部分,在Bean的創(chuàng)建、初始化和銷毀過(guò)程中,Spring提供了一系列的擴(kuò)展點(diǎn),其中,Bean Post Processor(后處理器)是一個(gè)重要的擴(kuò)展點(diǎn),它能夠在Bean的初始化前后做一些額外的處理,本文就和大家一起深入探究2023-07-07IDEA啟動(dòng)Springboot報(bào)錯(cuò):無(wú)效的目標(biāo)發(fā)行版:17 的解決辦法
這篇文章主要給大家介紹了IDEA啟動(dòng)Springboot報(bào)錯(cuò):無(wú)效的目標(biāo)發(fā)行版:17 的解決辦法,文中通過(guò)代碼示例和圖文講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(58)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-08-08SpringBoot中攔截器和動(dòng)態(tài)代理的區(qū)別詳解
在?Spring?Boot?中,攔截器和動(dòng)態(tài)代理都是用來(lái)實(shí)現(xiàn)功能增強(qiáng)的,所以在很多時(shí)候,有人會(huì)認(rèn)為攔截器的底層是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的,所以本文就來(lái)盤點(diǎn)一下他們兩的區(qū)別,以及攔截器的底層實(shí)現(xiàn)吧2023-09-09java 漢諾塔Hanoi遞歸、非遞歸(仿系統(tǒng)遞歸)和非遞歸規(guī)律 實(shí)現(xiàn)代碼
漢諾塔(Hanoi) 算法Java實(shí)現(xiàn)。通過(guò)三個(gè)函數(shù),分別對(duì)Hanoi進(jìn)行遞歸、非遞歸和非遞歸規(guī)律實(shí)現(xiàn)。2013-05-05spring?IOC容器的Bean管理XML自動(dòng)裝配過(guò)程
這篇文章主要為大家介紹了spring?IOC容器Bean管理基于XML的自動(dòng)裝配過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Spring?Boot項(xiàng)目Jar包加密實(shí)戰(zhàn)教程
本文詳細(xì)介紹了如何在Spring?Boot項(xiàng)目中實(shí)現(xiàn)Jar包加密,我們首先了解了Jar包加密的基本概念和作用,然后學(xué)習(xí)了如何使用Spring?Boot的Jar工具和第三方庫(kù)來(lái)實(shí)現(xiàn)Jar包的加密和解密,感興趣的朋友一起看看吧2024-02-02