欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 架构设计之自定义延迟双删缓存注解(下)

架构设计之自定义延迟双删缓存注解(下)

2025/4/1 3:28:30 来源:https://blog.csdn.net/weixin_42841433/article/details/146492906  浏览:    关键词:架构设计之自定义延迟双删缓存注解(下)

架构设计之自定义延迟双删缓存注解(下)

小薛博客官方架构设计之自定义延迟双删缓存注解(下)地址

为了保证@Cache@ClearAndReloadCache的灵活性,特意加入EL表达式解析

1、Cache

package com.xx.cache;import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;/*** @Author: xueqimiao* @Date: 2025/3/17 14:24*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {/*** 过期时间,默认60s* @return*/long expire() default 60 ;TimeUnit unit() default TimeUnit.SECONDS;/*** 缓存标识name* @return*/String name() default "";/*** SpringEL表达式,解析占位符对应的匹配value值* @return*/String matchValue();}

2、CacheAspect

package com.xx.cache;import com.xx.common.Result;
import com.xx.utils.RedisService;
import com.xx.utils.ValidationUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;/*** @Author: xueqimiao* @Date: 2025/3/17 14:25*/
@Component
@Aspect
@Slf4j
public class CacheAspect {@Resourceprivate RedisService redisService;/*** aop切点* 拦截被指定注解修饰的方法*/@Pointcut("@annotation(com.xx.cache.Cache)")public void cache() {}/*** 缓存操作** @param pjp* @return*/@Around("cache()")public Object toCache(ProceedingJoinPoint joinPoint) {Object result = null;try {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(),signature.getMethod().getParameterTypes());Cache annotation = method.getAnnotation(Cache.class);String matchedValue = annotation.matchValue();String keyPrefix = annotation.name();long time = annotation.expire();TimeUnit unit = annotation.unit();// 解析EL表达式SpelExpressionParser parser = new SpelExpressionParser();Expression expression = parser.parseExpression(matchedValue);EvaluationContext context = new StandardEvaluationContext();// 获取参数Object[] args = joinPoint.getArgs();DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();String[] parameterNames = discoverer.getParameterNames(method);for (int i = 0; i < parameterNames.length; i++) {context.setVariable(parameterNames[i], args[i]);}String key = keyPrefix + "::" + expression.getValue(context).toString();result = redisService.get(key);if (!ValidationUtil.isEmpty(result)) {return Result.ok(result);}// 执行目标方法result = joinPoint.proceed();Object res = result;if (result instanceof Result) {res = ((Result<?>) result).getResult();}redisService.set(key, res, time, unit);} catch (Throwable e) {throw new RuntimeException(e);}return result;}}

3、ClearAndReloadCache

package com.xx.cache;import java.lang.annotation.*;/*** @Author: xueqimiao* @Date: 2025/3/17 14:26*/
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface ClearAndReloadCache {/*** 缓存标识name* @return*/String name() default "";/*** SpringEL表达式,解析占位符对应的匹配value值* @return*/String matchValue();
}

4、ClearAndReloadCacheAspect

package com.xx.cache;import com.xx.utils.RedisUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** @Author: xueqimiao* @Date: 2025/3/17 14:26*/
@Aspect
@Component
@Slf4j
public class ClearAndReloadCacheAspect {@Resourceprivate RedisUtils redisUtils;/*** 切入点* 切入点,基于注解实现的切入点  加上该注解的都是Aop切面的切入点*/@Pointcut("@annotation(com.xx.cache.ClearAndReloadCache)")public void pointCut() {}/*** 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型** @param proceedingJoinPoint*/@Around("pointCut()")public Object aroundAdvice(ProceedingJoinPoint joinPoint) {log.info("----------- 环绕通知 -----------");log.info("环绕通知的目标方法名:" + joinPoint.getSignature().getName());try {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(),signature.getMethod().getParameterTypes());ClearAndReloadCache annotation = method.getAnnotation(ClearAndReloadCache.class);//反射得到自定义注解的方法对象String matchedValue = annotation.matchValue();String keyPrefix = annotation.name(); //获取自定义注解的方法对象的参数即name// 解析EL表达式SpelExpressionParser parser = new SpelExpressionParser();Expression expression = parser.parseExpression(matchedValue);EvaluationContext context = new StandardEvaluationContext();// 获取参数Object[] args = joinPoint.getArgs();DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();String[] parameterNames = discoverer.getParameterNames(method);for (int i = 0; i < parameterNames.length; i++) {context.setVariable(parameterNames[i], args[i]);}String key = keyPrefix + "::" + expression.getValue(context).toString();redisUtils.del(key);//模糊删除redis的key值//执行加入双删注解的改动数据库的业务 即controller中的方法业务Object proceed = null;try {proceed = joinPoint.proceed();//放行} catch (Throwable throwable) {throwable.printStackTrace();}//新开开一个线程延迟0.5秒(时间可以改成自己的业务需求),等着mysql那边业务操作完成//在线程中延迟删除  同时将业务代码的结果返回 这样不影响业务代码的执行new Thread(() -> {try {Thread.sleep(500);redisUtils.del(key);log.info("-----------0.5秒后,在线程中延迟删除完毕 -----------");} catch (InterruptedException e) {e.printStackTrace();}}).start();return proceed;//返回业务代码的值} catch (Throwable e) {throw new RuntimeException(e);}}
}

5、UserController

package com.xx.controller;import com.xx.cache.Cache;
import com.xx.cache.ClearAndReloadCache;
import com.xx.common.Result;
import com.xx.entity.User;
import com.xx.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;/*** @Author: xueqimiao* @Date: 2025/3/17 14:27*/
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate UserService userService;@PostMapping("/updateData")@ClearAndReloadCache(name = "user", matchValue = "#user.id")public Result updateData(@RequestBody User user) {return userService.update(user);}@GetMapping("/get")@Cache(name = "user", matchValue = "#id")public Result get(@RequestParam Integer id) {return userService.get(id);}}

6、RedisService

package com.xx.utils;import jakarta.annotation.Resource;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;/*** @Author: xueqimiao* @Date: 2023/7/17 13:46*/
@Component
public class RedisService {@Resourcepublic RedisTemplate redisTemplate;/*** 默认过期时长,单位:秒 一天*/public final static long DEFAULT_EXPIRE = 60 * 60 * 24;/*** 不设置过期时长*/public final static long NOT_EXPIRE = -1;private static double size = Math.pow(2, 32);/*=======================================String - Object=======================================*//*** 缓存基本的对象,Integer、String、实体类等** @param key* @param value* @return*/public boolean set(final String key, Object value) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);result = true;} catch (Exception e) {e.printStackTrace();}return result;}/*** 写入缓存设置时效时间** @param key* @param value* @return*/public boolean set(final String key, Object value, Long expireTime) {return set(key, value, expireTime, TimeUnit.SECONDS);}public boolean set(final String key, Object value, Long expireTime, TimeUnit unit) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);redisTemplate.expire(key, expireTime, unit);result = true;} catch (Exception e) {e.printStackTrace();}return result;}public <T> void setCacheObject(final String key, final T value) {redisTemplate.opsForValue().set(key, value);}/*** 获得缓存的Object对象** @param key 缓存的键值* @return 缓存键值对应的数据*/public Object getCache(final String key) {return redisTemplate.opsForValue().get(key);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key) {ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T get(final String key) {ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 读取缓存** @param key* @return*/public <T> T get(final String key, Class<T> clazz) {ValueOperations<Serializable, Object> operation = redisTemplate.opsForValue();T result = (T) operation.get(key);return result;}/*** 设置有效时间** @param key     Redis键* @param timeout 超时时间* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout) {return expire(key, timeout, TimeUnit.SECONDS);}/*** 设置有效时间** @param key     Redis键* @param timeout 超时时间* @param unit    时间单位* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit) {return redisTemplate.expire(key, timeout, unit);}/*** 获取有效时间** @param key Redis键* @return 有效时间*/public long getExpire(final String key) {return redisTemplate.getExpire(key);}/*** 判断 key是否存在** @param key 键* @return true 存在 false不存在*/public Boolean hasKey(String key) {return redisTemplate.hasKey(key);}/*** 删除单个对象** @param key*/public void delete(String key) {redisTemplate.delete(key);}/*** 删除集合对象** @param collection*/public void delete(Collection collection) {redisTemplate.delete(collection);}/*** 判断缓存中是否有对应的value** @param key* @return*/public boolean exists(final String key) {return redisTemplate.hasKey(key);}/*** 累加 1** @param key 缓存的键值* @return*/public Long increment(final String key) {return increment(key, 1L);}public Long decrement(final String key) {return decrement(key, 1L);}/*** 累加 指定值** @param key 缓存的键值* @param num 累加的数量* @return*/public Long increment(final String key, Long num) {return redisTemplate.opsForValue().increment(key, num);}public Long decrement(final String key, Long num) {return redisTemplate.opsForValue().decrement(key, num);}/*** 获得缓存的基本对象key列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern) {return redisTemplate.keys(pattern);}/*=======================================List=======================================*//*** 缓存List数据** @param key      缓存的键值* @param dataList 待缓存的List数据* @return 缓存的对象*/public <T> long listRightPush(final String key, final List<T> dataList) {Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}public long listRightPush(String key, String value) {Long listSize = redisTemplate.opsForList().rightPush(key, value);return null == listSize ? 0 : listSize;}public <T> long listLeftPush(final String key, final List<T> dataList) {Long count = redisTemplate.opsForList().leftPushAll(key, dataList);return count == null ? 0 : count;}public long listLeftPush(String key, String value) {Long listSize = redisTemplate.opsForList().leftPush(key, value);return null == listSize ? 0 : listSize;}public long listRemove(String key, long count, String value) {Long remove = redisTemplate.opsForList().remove(key, count, value);return null == remove ? 0 : remove;}/*** 获得缓存的list对象** @param key 缓存的键值* @return 缓存键值对应的数据*/public <T> List<T> getCacheList(final String key) {return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存List数据** @param key      缓存的键值* @param dataList 待缓存的List数据* @return 缓存的对象*/public <T> long setCacheList(final String key, final List<T> dataList) {Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/*** 列表获取** @param k* @param start* @param end* @return*/public <T> List<T> listRange(String k, long start, long end) {ListOperations<String, T> list = redisTemplate.opsForList();return list.range(k, start, end);}public <T> List<T> listRange(String k) {return listRange(k, 0, -1);}/*=======================================Set=======================================*//*** 缓存Set** @param key     缓存键值* @param dataSet 缓存的数据* @return 缓存数据的对象*/public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()) {setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** @param key* @return*/public <T> Set<T> getCacheSet(final String key) {return redisTemplate.opsForSet().members(key);}/*** 集合添加** @param key* @param value*/public void add(String key, Object value) {SetOperations<String, Object> set = redisTemplate.opsForSet();set.add(key, value);}/*** 集合获取** @param key* @return*/public Set<Object> setMembers(String key) {SetOperations<String, Object> set = redisTemplate.opsForSet();return set.members(key);}public <T> T pop(String key) {SetOperations<String, T> set = redisTemplate.opsForSet();return set.pop(key);}public <T> List<T> pop(String key, Long num) {SetOperations<String, T> set = redisTemplate.opsForSet();return set.pop(key, num);}/*=======================================ZSet=======================================*//*** 有序集合添加** @param key* @param value* @param scoure*/public void zAdd(String key, Object value, double scoure) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();zset.add(key, value, scoure);}/*** 有序集合获取** @param key* @param scoure* @param scoure1* @return*/public Set<Object> rangeByScore(String key, double scoure, double scoure1) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();redisTemplate.opsForValue();return zset.rangeByScore(key, scoure, scoure1);}/*** 有序集合获取排名** @param key   集合名称* @param value 值*/public Long zRank(String key, Object value) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();return zset.rank(key, value);}/*** 有序集合获取排名** @param key*/public Set<ZSetOperations.TypedTuple<Object>> zRankWithScore(String key, long start, long end) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();Set<ZSetOperations.TypedTuple<Object>> ret = zset.rangeWithScores(key, start, end);return ret;}/*** 有序集合添加** @param key* @param value*/public Double zSetScore(String key, Object value) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();return zset.score(key, value);}/*** 有序集合添加分数** @param key* @param value* @param scoure*/public void incrementScore(String key, Object value, double scoure) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();zset.incrementScore(key, value, scoure);}/*** 有序集合获取排名** @param key*/public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithScore(String key, long start, long end) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeByScoreWithScores(key, start, end);return ret;}/*** 有序集合获取排名** @param key*/public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithRank(String key, long start, long end) {ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeWithScores(key, start, end);return ret;}/*=======================================Hash=======================================*//*** 缓存Map** @param key* @param dataMap*/public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** @param key* @return*/public <T> Map<String, T> getCacheMap(final String key) {return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** @param key   Redis键* @param hKey  Hash键* @param value 值*/public <T> void setCacheMapValue(final String key, final String hKey, final T value) {redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** @param key  Redis键* @param hKey Hash键* @return Hash中的对象*/public <T> T getCacheMapValue(final String key, final String hKey) {HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 获取多个Hash中的数据** @param key   Redis键* @param hKeys Hash键集合* @return Hash对象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 哈希 添加** @param key* @param hashKey* @param value*/public void hmSet(String key, Object hashKey, Object value) {HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();hash.put(key, hashKey, value);}/*** 哈希获取数据** @param key* @param hashKey* @return*/public Object hmGet(String key, Object hashKey) {HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();return hash.get(key, hashKey);}/*=======================================Bit=======================================*//*** 写入缓存** @param key* @param offset 位 8Bit=1Byte* @return*/public boolean setBit(String key, long offset, boolean isShow) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.setBit(key, offset, isShow);result = true;} catch (Exception e) {e.printStackTrace();}return result;}/*** 写入缓存** @param key* @param offset* @return*/public boolean getBit(String key, long offset) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();result = operations.getBit(key, offset);} catch (Exception e) {e.printStackTrace();}return result;}public void saveDataToRedis(String name) {Double index = Math.abs(name.hashCode() % size);long indexLong = index.longValue();boolean availableUsers = setBit("availableUsers", indexLong, true);}public boolean getDataToRedis(String name) {Double index = Math.abs(name.hashCode() % size);long indexLong = index.longValue();return getBit("availableUsers", indexLong);}
}

博客心语

在Java编程的征程中,我们都是追梦人。每一次编写代码,都是在编织一个关于未来的故事。这个故事可能是一个高效的电商系统,让购物变得更加便捷;也可能是一个智能的医疗应用,拯救无数的生命。无论你的目标是什么,Java都是你实现梦想的有力武器。所以,不要停下你前进的脚步,不断探索Java的深度和广度,让你的代码充满生命力,因为你正在用一种伟大的语言塑造着一个充满无限可能的未来。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词