栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

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

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 配置信息绑定类
@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
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;
           }
       }


    }

    
    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;
    }

    
    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 {

    
    T getDataFromCache(String spelValue, TypeReference typeReference) throws Exception;

    
    RBloomFilter getBloomFilter(String bloomName);

    
    RLock getLock(String lockKey);

    
    void saveCacheData(String cacheKey, Object detail, Type returnType) throws Exception;
}
CacheServiceImpl 实现类
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;
    }


    
    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;

    }

    
    public RBloomFilter getBloomFilter(String bloomName) {
        RBloomFilter bloomFilter = redissonClient.getBloomFilter(bloomName);
        return bloomFilter;
    }

    
    public RLock getLock(String lockKey) {
        return redissonClient.getLock(lockKey);
    }

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

        }


    }
}



转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号