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

AOP+自定义注解实现redis缓存自动更新

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

AOP+自定义注解实现redis缓存自动更新

1、前言

在前台查询首页数据的方法上加了注解:

@Cacheable(value = "courseAndTeacher", key = "'selectIndexList'")

另外还有两个注解是用在更新缓存:

本来只要这几个注解配合起来使用就能实现数据自动更新,但是由于前台数据只需要查数据,而后台则有全部的增删改查,接口比较多,许多方法都要加注解,容易疏漏也不方便管理,所以想写个加在控制类上的注解,一个注解搞定自动更新缓存

网上比较流行的那种写法,是对每个方法加都实现缓存操作,但是并没有实际的用处,因为每个方法都有自己的key值而不会去更新指定的缓存。

不过参考那种写法,有了个新思路:前台数据查询时,缓存注解还是使用原来的@Cacheable,然后仿照这个注解增加个自定义注解,也能传入key,value的值,这样的话可以拼接出需要自动更新的缓存的key值,然后直接清除这个缓存,利用aop切面对标注了这个注解的类中所有方法中的增删改操作都会去清除缓存,该注解适合直接加在后台控制类上

下面是实现

2、自定义注解:RedisCutCache

@documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCutCache {

    String key() default "";

    String value() default "default";
}
3、编写切面类:RedisCutCacheAOP

import com.yuanhan.baseservice.aop.redisaop.annotation.RedisCutCache;
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.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.regex.Pattern;


@Component
@Aspect
@Slf4j
public class RedisCutCacheAOP {

    
    private static final Pattern SET_CACHE_PATTERN = Pattern.compile("^((add)|(insert)|(save)|(batchInsert)|(batchUpdate)|(update)|(delete)|(remove)).*$");


    @Resource
    private RedisTemplate redisTemplate;

    
    @Pointcut("@within(com.yuanhan.baseservice.aop.redisaop.annotation.RedisCutCache)")
    public void queryCachePointcut(){

    }

    @Around("queryCachePointcut()&&@within(redisCutCache)")
    public Object Interceptor(ProceedingJoinPoint joinPoint, RedisCutCache redisCutCache) throws Throwable{
        long beginTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法名
        String methodName = signature.getMethod().getName();
        //动态获取注解中的数据
        String prekey = redisCutCache.key().replace("'","");
        String value = redisCutCache.value();
        //拼接key值
        String key = value + "::" + prekey;

        if(SET_CACHE_PATTERN.matcher(methodName).matches()){
            if (redisTemplate.hasKey(key)){
                redisTemplate.delete(key);
                log.warn("执行方法 : [ "+methodName+" ] :  清除 key 为 ["+key+ "] :的缓存数据");
            }else {
                log.warn("执行方法 : [ "+methodName+" ] :  没有 key 为 ["+key+ "] :的缓存数据");
            }
            log.warn("AOP 清除缓存 >>>> end 耗时:" + (System.currentTimeMillis() - beginTime) + "ms.");
        }
        // 调用原始方法
        return joinPoint.proceed();
    }

//    @Autowired(required = false)
//    public void setRedisTemplate(RedisTemplate redisTemplate) {
//        RedisSerializer stringSerializer = new StringRedisSerializer();//序列化为String
//        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//序列化为Json
//        redisTemplate.setKeySerializer(stringSerializer);
//        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//        redisTemplate.setHashKeySerializer(stringSerializer);
//        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
//        this.redisTemplate = redisTemplate;
//    }


}

下图是需要注意的地方

 4、配置redis配置类:RedisConfig

上面的代码中我把有关RedisTemplate的配置注释掉了,因为我配置过了,使用起来没有问题,这里我把配置类也贴出来,或者不加配置类把那段注释打开,具体效果如何我没测试过


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@EnableCaching //开启缓存
@Configuration  //配置类
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate template = new RedisTemplate<>();
        RedisSerializer redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //enableDefaultTyping(...) 建议改成 activateDefaultTyping(om.getPolymorphicTypevalidator(), ...)
        om.activateDefaultTyping(om.getPolymorphicTypevalidator(),ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypevalidator(),ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializevaluesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}
5、需要用到的依赖

可能有一些漏掉的,需要自己补充了

        
        
            org.springframework.boot
            spring-boot-starter-aop
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
6、使用

在前台查询数据接口上添加注解

@Cacheable(value = "courseAndTeacher", key = "'selectIndexList'")

 这是在redis中生成缓存的key

 可以看到拼接规则是:

String key = value + "::" + prekey;

所以想要做到及时更新这个缓存,只需要同样的传入value和key,然后去清空缓存

在后台增删改查的控制类上添加注解:

@RedisCutCache(value = "courseAndTeacher", key = "'selectIndexList'")

 这样只要数据有了变化(增删改),就会自动清除缓存,查询接口就会从数据库中取出最新的值

这里有个细节

 为了保持一致性(其实是为了方便直接复制),我们自定义注解也是这样传参数,但是会在取值时过滤掉单引号,这是自定义注解类中的源码

 到这一步就可以大功告成了

7、实战效果

控制台打印了日志

清空控制台,不断刷新首页 ,无日志显示

使用缓存查询前台数据生效

然后在后台对课程信息进行操作,比如更改课程标题,控制台打印

查看缓存

 

课程跟讲师相关缓存已清除,下次访问首页即可从数据库中获取最新的数据,完成! 

8、总结

1、编写自定义注解:RedisCutCache

2、编写切面类:RedisCutCacheAOP

3、检查是否配置redis配置类,没有的话把切面类下面的注释打开(这个方式本人未尝试,无用的话建议复制我的配置类)

4、在查询接口方法上添加注解:@Cacheable(value = "xxx", key = "'xxx'")

6、在后台控制类(是类注解)上添加注解:@RedisCutCache(value = "xxx", key = "'xxx'")

注意这两个注解的value和key要完全一样,因为是为了操作同一个key的缓存,一个是用在前台查询方法上,另一个是用在后台控制类上

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

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

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