目录
一、基于RedisTemplate方式
1、业务描述
2、业务逻辑代码
3、自定义配置类
二、基于AOP方式
1、业务描述
2、业务逻辑代码
3、自定义配置类
3.1、定义key生成器
3.2、定义cache管理对象
三、加入本地缓存策略
1、业务描述
2、业务实现
一、基于RedisTemplate方式
1、业务描述
RedisTemplate方式实现业务操作优先查询redis缓存,如果redis缓存中没有数据再查询mysql数据库。
2、业务逻辑代码
//注入RedisTemplate对象
@Autowired
private RedisTemplate redisTemplate;
//业务处理
@Override
public List<Tag> selectTags() {
//1.从redis查询Tag信息,redis有则直接返回
ValueOperations<String,List<Tag>> valueOperations =
redisTemplate.opsForValue();
List<Tag> tags=valueOperations.get("tags");
if(tags!=null&&!tags.isEmpty())return tags;
//2.从redis没有获取tag信息,查询mysql
tags = tagMapper.selectList(null);
//3.将从mysql查询到tag信息存储到redis
valueOperations.set("tags", tags);
//4.返回查询结果
return tags;
}
3、自定义配置类
1、自定义RedisTemplate的泛型,默认泛型为Object,实际业务中需要进行自定义配置。
默认为:RedisTemplate<Object,Object>
修改为:RedisTemplate<String, ? extends Object>
其中: ? extends Object 意为上界限定通配符
2、自定义RedisTemplate中的序列化方式,Redis容易默认采用的是JDK的序列化方式,非Java程序读取的情况下无法阅读容器中的对象。
具体代码如下:
/**
* 1、自定义RedisTemplate的泛型
* 2、自定义RedisTemplate中的序列化方式
* 代码定制参考RedisAutoConfiguration类
*/
@Bean
@ConditionalOnMissingBean(name = {"redisTemplate"})
public RedisTemplate<String, ? extends Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
//定义RedisTemplate的泛型类型
RedisTemplate<String, ? extends Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
//定义redisTemplate对象的序列号方式
//1、定义key的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
//2、定义value的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(
objectMapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
//3.修改redisTemplate默认样式
template.afterPropertiesSet();//after表示除了序列化,其他原有特性不丢
template.setEnableTransactionSupport(true);
return template;
}
二、基于AOP方式
1、业务描述
AOP方式实现业务操作优先查询redis缓存,如果redis缓存中没有数据再查询mysql数据库。
2、业务逻辑代码
只需在业务方法上添加 @Cacheable?注解即可,value的值为redis容器中key的值。
注:@Cacheable?注解生效前提需要在启动类或者配置类上添加?@EnableCaching?注解。
/**
* @Cacheable注解描述的方法为缓存切入点方法
* 访问此方法时系统底层会先从缓存查找数据,假如缓存没有会查询mysql
* 这个注解假如想生效需要在启动类或者配置上添加@EnableCaching注解
*/
@Cacheable(value = "tagCache")
@Override
public List<Tag> selectTags() {
return tagMapper.selectList(null);
}
3、自定义配置类
3.1、定义key生成器
默认key可读性较差。
需要注意的是定义 KeyGenerator?bean对象时配置类需要继承CachingConfigurerSupport?类。
/**
* 自定义缓存key的生成器,不定义也可以使用默认的key
* 注:定义KeyGenerator对象key生成器时,配置类需要继承CachingConfigurerSupport对象
*/
@Bean
public KeyGenerator keyGenerator(){
return (o, method, objects) -> {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder
.append(o.getClass().getName())
.append("::")
.append(method.getClass().getName());
for (Object object : objects) { //方法没有参数就没有这个循环了
stringBuilder.append(object);
}
return stringBuilder.toString();
};
}
3.2、定义cache管理对象
在AOP方式下使用的序列化方式默认也是JDK方式,需要转换为Json方式的话也需要自己配置。
/**
* 自定义Cache管理器对象,按照指定的序列化方式进行序列化,
* 将序列化修改为Json方式(redis默认的序列化方式是JDK序列化)
* redis默认的序列化方式是JDK序列化
*/
@Bean
public CacheManager CacheManager(RedisConnectionFactory redisConnectionFactory){
RedisCacheConfiguration config = RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(60))//设置key的有效时间,可根据业务需求设置
//配置key的序列化
.serializeKeysWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new StringRedisSerializer()))
//配置value的序列化
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class)))
.disableCachingNullValues();//不存储空值
return RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(config)//修改默认对象
.transactionAware()//启动默认事物
.build();
}
三、加入本地缓存策略
1、业务描述
查询优先查询本地缓存,如果本地缓存没有数据查询redis缓存,?如果redis缓存没有数据最后再查询数据库。
2、业务实现
1、使用 CopyOnWriteArrayList 对象,相对于 ArrayList?这种方式线程安全。
2、采用了双重校验的方法,保证线程安全,同时减少阻塞。
@RestController
@RequestMapping("/tag")
public class TagController {
@Autowired
private TagService tagService;
//此对象存在JVM
private List<Tag> tags=new CopyOnWriteArrayList<>();//本地 cache
@GetMapping
public List<Tag> doSelectTags(){
if(tags.isEmpty()) {
synchronized (tags) {
if(tags.isEmpty()) {
tags.addAll(tagService.selectTags());//1.redis,2.mysql
}
}
}
return tags;
}
}
序列化:
狭义层面:将对象转换为字节(I/O 相当于序列化的子集)
广义层面:将对象转换为指定格式字符串(例如:json)
封装:
狭义层面:属性私有化,方法能公开则公开
广义层面:一个系统有哪些服务(子系统),一个服务有哪些模块,一个模块有哪些对象,一个对象有哪些属性
|