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

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

SpringBoot自定义分布式缓存starter 并用@Enablexx 控制缓存功能是否启用_第1张图片

1.创建maven项目,添加依赖

项目结构

SpringBoot自定义分布式缓存starter 并用@Enablexx 控制缓存功能是否启用_第2张图片

pom.xml



    4.0.0
    com.sgg
    aopCache-spring-boot-start
    0.0.1-SNAPSHOT
    aopCache-spring-boot-start
    aopCache-spring-boot-start

    
        1.8
        UTF-8
        UTF-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 {

    //需要 redisTemplate  redissonClient

    @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 is  empty");
        }
        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/13  17: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=null;

        if (!StringUtils.isEmpty(bloomValueExpr)){
            String bloomVlaueStr = parseSpel(bloomValueExpr, joinPoint,"bloomValue");
             bloomValue = 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 = 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/13  13:29
 */
public class CacheServiceImpl implements CacheService {

    StringRedisTemplate redisTemplate; //操作redis

    RedissonClient redissonClient; //使用redisson

    ObjectMapper 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 = 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);

        }


    }
}

 
  

                            
                        
                    
                    
                    

你可能感兴趣的:(SpringBoot,redis,缓存,分布式)