springboot 2.x入门(四)—springboot2.x 基于注解的缓存实现并增加过期时间

     晚上去参加了一个本田的车友会,一水的思域,虽然我不是本田车主啊,但是因为我非常喜欢车,所以有这种聚会都会去凑凑热闹,两厢思域红色是真的帅,哈哈哈,晚上快十点才到家,这篇文章写完都十二点了,磨磨蹭蹭的。好了,废话不多说了进入正题。    

    今天是springboot2.x整合redis的第二节,基于注解实现缓存,其实spring的CacheManager支持多种缓存组件,像ehcache,jcache,redis,但是redis最火嘛,所以我们就拿它来举例了,另外正是因为支持多种缓存数据库,各数据库的ttl机制又不一样,所以spring 提供的注解并没有ttl的设置参数,在实际应用当中很不方便,所以今天我们就通过复写CacheManager增加注解的ttl配置。

    spring 从3.1开始就基于JSR107规范实现了自己的缓存注解,其中包含Cacheable、CachePut、CacheEvict三个常用注解。

    首先要想开启缓存管理器,需要在启动类上增加@EnableCaching注解

@EnableCaching
@SpringBootApplication
public class SpringbootRedisApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootRedisApplication.class, args);
	}

}

@Cacheable
该注解可以作用在类或方法上, 在类上表示所有方法返回对象都加入缓存, 如果在方法上则指定该方法返回对象加入缓存。

@CachePut
该注解与@Cacheable类似,区别在于它会每次都执行,并且替换缓存中相同名称,一般用来更新缓存。

@CacheEvict
该注解用来清除缓存,它的属性与@Cacheable相比多了两个,allEntries 默认 false,根据 key 来清除指定缓存,如果为 true 则表示忽略 key 属性,对所有缓清除。beforeInvocation 属性为 true 时表示,在执行该方法之前执行清除缓存操作。

    解释完了,我们进入实操阶段,今天我们只展开讲@Cacheable,会用这个了,另外两个也就会用了。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};

    String key() default "";

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";

    String condition() default "";

    String unless() default "";

    boolean sync() default false;
}

    以上是@Cacheable注解的参数,常用的是key、value、condition、unless这四个key就是redis键值,value是缓存的命名空间,condition是前置条件,unless是后置条件,过滤数据。在注解的参数中可以使用SpEl表达式进行动态拼接,下面我提供一些测试代码,给大家演示一下。

@Service
public class TestServiceImpl implements TestService{
    
    //常规姿势
    @Override
    @Cacheable(value = {"test"}, key = "#id")
    public String test(Integer id) {
        return "test";
    }

    //带有字符串拼接
    @Override
    @Cacheable(value = {"test1"}, key = "'id-' + #id")
    public String test1(Integer id) {
        return "test";
    }

    //只缓存id>1的
    @Override
    @Cacheable(value = {"test2"}, key = "'hash' + #id",condition = "#id > 1")
    public String test2(Integer id) {
        return "test";
    }

    //结果为null的不缓存
    @Override
    @Cacheable(value = {"test3"}, key = "#id", unless = "#result != null")
    public String test3(Integer id) {
        return null;
    }
}

    以上就是Cacheable的使用方法,是不是很方便,但是就是不能设置缓存的失效时间,接下来我们就来实现CacheManager来实现。接下来我们要对上一节的RedisConfig类进行改造,上代码!!!!!!

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
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.cache.RedisCacheWriter;
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.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(30 * 60), // 默认策略,未配置的 key 会使用这个
                this.getRedisCacheConfigurationMap() // 指定 key 策略
        );
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        //SsoCache和BasicDataCache进行过期时间配置
        redisCacheConfigurationMap.put("test", this.getRedisCacheConfigurationWithTtl(24 * 60 * 60));
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));

        return redisCacheConfiguration;
    }

    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append("." + method.getName());
                if (params == null || params.length == 0 || params[0] == null) {
                    return null;
                }
                String join = String.join("&", Arrays.stream(params).map(Object::toString).collect(Collectors.toList()));
                String format = String.format("%s{%s}", sb.toString(), join);
                return format;
            }
        };
    }

}

    上面代码中,我们在CacheManager中进行了缓存时间的配置,对统一命名空间的缓存进行了统一的失效时间处理。我们在使用中,只需要将注解中的value配置在RedisConfig类中,就完成了失效时间的配置。

    欢迎大家关注我的公众号:codesls,让我们 一起嗨嗨皮皮。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值