springboot的一些新特性及用法(springboot2.x入门四)
springboot的一些新特性及用法(springboot2.x入门四)@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 "
晚上去参加了一个本田的车友会,一水的思域,虽然我不是本田车主啊,但是因为我非常喜欢车,所以有这种聚会都会去凑凑热闹,两厢思域红色是真的帅,哈哈哈,晚上快十点才到家,这篇文章写完都十二点了,磨磨蹭蹭的。好了,废话不多说了进入正题。
今天是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类中,就完成了失效时间的配置。