马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
架构计划之自定义延迟双删缓存注解(下)
小薛博客官方架构计划之自定义延迟双删缓存注解(下)所在
为了包管@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 {
- @Resource
- private 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 {
- @Resource
- private 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 {
- @Resource
- private 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 {
- @Resource
- public 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的深度和广度,让你的代码充满生命力,由于你正在用一种巨大的语言塑造着一个充满无限可能的未来。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|