SpringBoot|SpringBoot自定义分布式缓存starter 并用@Enablexx 控制缓存功能是否启用

自定义Redis分布式缓存starter 并使用@Enablexx 控制缓存功能是否启用 SpringBoot|SpringBoot自定义分布式缓存starter 并用@Enablexx 控制缓存功能是否启用
文章图片

1.创建maven项目,添加依赖 项目结构
【SpringBoot|SpringBoot自定义分布式缓存starter 并用@Enablexx 控制缓存功能是否启用】SpringBoot|SpringBoot自定义分布式缓存starter 并用@Enablexx 控制缓存功能是否启用
文章图片

pom.xml

4.0.0 com.sgg aopCache-spring-boot-start 0.0.1-SNAPSHOT aopCache-spring-boot-start aopCache-spring-boot-start1.8UTF-8UTF-8 2.3.7.RELEASE org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 2.6.0 org.redisson redisson 3.15.3 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import

2.自定义注解 1.@EnableGmalllCache 启动功能注解
package com.sgg.aopcache.cache.anno; import com.sgg.aopcache.cache.selector.AopCacheSelector; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Target(ElementType.TYPE) @Documented @Retention(RetentionPolicy.RUNTIME) @Import(AopCacheSelector.class) public @interface EnableGmalllCache { }

2.@GmalllCache 目标方法缓存注解
@Target({ ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GmalllCache {String cacheKey() default ""; //支持写表达式String bloomName() default ""; //默认是不用布隆String bloomValue() default ""; //布隆需要判断哪个值的表达式String lockKey() default ""; // 分布式锁值}

3.配置类 1.RedissonProperties 配置信息绑定类
/** * redisson配置信息 */ @Data @ConfigurationProperties("spring.redis") public class RedissonProperties {private String host = "localhost"; private String addresses = ""; private String password = ""; private String port = "6379"; private int timeout = 3000; private int connectionPoolSize = 64; private int connectionMinimumIdleSize=10; private int pingConnectionInterval = 60000; public static String ADDRESS_PREFIX = "redis://"; }

2. AopCacheConf 组件配置类
public class AopCacheConf {//需要 redisTemplateredissonClient@Bean public CacheService cacheService(StringRedisTemplate stringRedisTemplate,RedissonClient redissonClient) { if (StringUtils.isEmpty(redissonClient)){ throw new RuntimeException("请往容器中添加RedissonClient组件"); } System.out.println("cacheService方法执行了,往容器中添加CacheService组件"); return new CacheServiceImpl(stringRedisTemplate,redissonClient); }@Bean public GmallCacheAspect gmallCacheAspect(CacheService cacheService) { System.out.println("GmallCacheAspect方法执行了,往容器中添加GmallCacheAspect组件"); return new GmallCacheAspect(cacheService); }}

3.RedissonAotoConfiguration 自动装配类
@EnableConfigurationProperties(RedissonProperties.class) public class RedissonAotoConfiguration {/** * 自动装配 * */@Bean RedissonClient redissonClient(RedissonProperties redissonConfig) { Config config = new Config(); if(StringUtils.isEmpty(redissonConfig.getHost())){ throw new RuntimeException("host isempty"); } SingleServerConfig serverConfig = config.useSingleServer() .setAddress(RedissonProperties.ADDRESS_PREFIX + redissonConfig.getHost() + ":" +redissonConfig.getPort()) .setTimeout(redissonConfig.getTimeout()) .setPingConnectionInterval(redissonConfig.getPingConnectionInterval()) .setConnectionPoolSize(redissonConfig.getConnectionPoolSize()) .setConnectionMinimumIdleSize(redissonConfig.getConnectionMinimumIdleSize()) ; if(!StringUtils.isEmpty(redissonConfig.getPassword())) { serverConfig.setPassword(redissonConfig.getPassword()); } // RedissonClient redisson = Redisson.create(config); System.out.println("RedissonClient组件创建完成"); return Redisson.create(config); }}

4.Selector
/** * @author sz * @DATE 2022/3/1317:54 */ public class AopCacheSelector implements ImportSelector {public String[] selectImports(AnnotationMetadata importingClassMetadata) { //要添加的组件的全限定类名 System.out.println("selectImports方法执行了,往容器中添加AopCacheConf组件"); return new String[]{"com.sgg.aopcache.cache.conf.AopCacheConf"}; } }

5.切面类
@Slf4j @Aspect public class GmallCacheAspect {//没抢到默认的等待时间 private AtomicLong atomicLong = new AtomicLong(1000); private CacheService cacheService; public GmallCacheAspect(CacheService cacheService) { this.cacheService = cacheService; }public GmallCacheAspect() {}@Around("@annotation(com.sgg.aopcache.cache.anno.GmalllCache)") public Object around(ProceedingJoinPoint joinPoint) throws Exception { //先拿到目标方法执行的参数 Object[] args = joinPoint.getArgs(); //拿到方法声明 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //拿到方法声明上注解 Method method = signature.getMethod(); //获取方法方法的返回值类型 final Type returnType = method.getGenericReturnType(); GmalllCache cacheAnnotation = method.getAnnotation(GmalllCache.class); //拿到注解上的值缓存数据的key String cacheKeyExpr = cacheAnnotation.cacheKey(); String bloomValueExpr = cacheAnnotation.bloomValue(); //Sp EL表达式 解析 String cacheKey = parseSpel(cacheKeyExpr, joinPoint,"cacheKey"); Long bloomValue=https://www.it610.com/article/null; if (!StringUtils.isEmpty(bloomValueExpr)){ String bloomVlaueStr = parseSpel(bloomValueExpr, joinPoint,"bloomValue"); bloomValue = https://www.it610.com/article/Long.valueOf(bloomVlaueStr); }while (true){ //从缓冲中拿数据并根据方法的返回值类型封装数据 Object dataFromCache = cacheService.getDataFromCache(cacheKey, new TypeReference() { @Override public Type getType() { //返回方法对应的返回类型 return returnType; } }); if (StringUtils.isEmpty(dataFromCache)) { //缓存中没有数据回源经过布隆过滤器和锁 boolean contains = true; if (!StringUtils.isEmpty(bloomValue)){RBloomFilter bloomFilter = cacheService.getBloomFilter(cacheAnnotation.bloomName()); //布隆过滤器判断是否有值 contains = bloomFilter.contains(bloomValue); }if (contains) { //如果布隆过滤器中有这个对象 //获取注解的分布式锁 lockKey的值 String lockKeyExpr = cacheAnnotation.lockKey(); String lockKey = (String) parseSpel(lockKeyExpr, joinPoint,"lockKey"); //连接点方法的返回值对象 Object detail = null; //如果锁的值为空不使用分布式锁直接查数据库 if (StringUtils.isEmpty(lockKey)) { detail = getProceed(joinPoint); } else { //拿到锁对象 RLock rLock = cacheService.getLock(lockKey); boolean tryLockFlag = false; try { //尝试加锁 tryLockFlag = rLock.tryLock(); //如果加锁成功 if (tryLockFlag) { long curr = System.currentTimeMillis(); //查询数据库也就是执行目标方法 detail = getProceed(joinPoint); atomicLong.set(System.currentTimeMillis() - curr); // //将切入点方法查询到的数据存入缓存 cacheService.saveCacheData(cacheKey, detail, returnType); return detail; } else { //如果加锁失败等待一秒后重新查询 TimeUnit.MINUTES.sleep(atomicLong.get()); }} catch (InterruptedException e) { log.error("加锁出现了异常"); throw new RuntimeException(e); } finally {try { //加锁成功后解锁 if (tryLockFlag) { rLock.unlock(); } } catch (Exception e) { log.error("解错了锁"); throw new RuntimeException(e); } } } } else { //如果布隆过滤器中没有,返回空对象 return null; } } else { //缓存中有数据返回缓存中数据 return dataFromCache; } }}/** * 执行目标方法获取返回数据 * * @param joinPoint * @return */ private Object getProceed(ProceedingJoinPoint joinPoint) {Object proceed = null; try { //前置通知Object[] args = joinPoint.getArgs(); proceed = joinPoint.proceed(args); //返回后通知 } catch (Throwable e) { //异常通知 throw new RuntimeException(e); } finally { //后置通知 }return proceed; }/** * 解析spel表达式的值 * * @param cacheKey * @return */ private String parseSpel(String cacheKey, ProceedingJoinPoint joinPoint,String type) {//拿到方法的参数值 Object[] args = joinPoint.getArgs(); //拿到方法对象 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); if (args.length==0){ if (type.contains("lockKey")){ return signature.getMethod().getName()+":lockKey:"; } if (type.contains("cacheKey")){ return signature.getMethod().getName()+":cacheKey:"; } }ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); evaluationContext.setVariable("args", args); evaluationContext.setVariable("method", signature); String value = https://www.it610.com/article/parser.parseExpression(cacheKey, new TemplateParserContext()) .getValue(evaluationContext, String.class); return value; }}
6.CacheService CacheService接口
public interface CacheService {/** * 从缓冲中获取数据 * @param spelValue * @param typeReference * @return */ T getDataFromCache(String spelValue, TypeReference typeReference) throws Exception; /** * 获取布隆过滤器名 * @param bloomName */ RBloomFilter getBloomFilter(String bloomName); /** * 获取一把分布式锁 * @param lockKey * @return */ RLock getLock(String lockKey); /** * 将数据存入缓存 * @param cacheKey * @param detail * @param returnType */ void saveCacheData(String cacheKey, Object detail, Type returnType) throws Exception; }

CacheServiceImpl 实现类
/** * @author sz * @DATE 2022/3/1313:29 */ public class CacheServiceImpl implements CacheService {StringRedisTemplate redisTemplate; //操作redisRedissonClient redissonClient; //使用redissonObjectMapper objectMapper = new ObjectMapper(); public CacheServiceImpl(StringRedisTemplate redisTemplate, RedissonClient redissonClient) { this.redisTemplate = redisTemplate; this.redissonClient = redissonClient; }/** * 从缓冲获取数据 * * @param spelValue * @param returnType * @return */ public T getDataFromCache(String spelValue, TypeReference returnType) throws Exception { String json = redisTemplate.opsForValue().get(spelValue); if (StringUtils.isEmpty(json)) { return null; }T value = https://www.it610.com/article/objectMapper.readValue(json, returnType); return value; }/** * 获取布隆过滤器 * * @param bloomName */ public RBloomFilter getBloomFilter(String bloomName) { RBloomFilter bloomFilter = redissonClient.getBloomFilter(bloomName); return bloomFilter; }/** * 获取分布式锁 * * @param lockKey * @return */ public RLock getLock(String lockKey) { return redissonClient.getLock(lockKey); }/** * 将数据存入缓存 * @param cacheKey * @param detail * @param returnType */ public void saveCacheData(String cacheKey, Object detail, Type returnType) throws Exception {//要缓存的数据 Object target = detail; //缓存空值 if (StringUtils.isEmpty(target)) { //如果从数据库中查询的数据时null redisTemplate.opsForValue().set(cacheKey,"n",30, TimeUnit.MINUTES); }else {String writeValueAsString = new ObjectMapper().writeValueAsString(target); redisTemplate.opsForValue().set(cacheKey, writeValueAsString,3, TimeUnit.DAYS); }} }

    推荐阅读