1.简单介绍缓存的注解。
类注解:
@EnableCaching:标注在启动类或配置类上,表示开启缓存。
方法注解:
@Cacheable:使用该注解的方法优先从缓存中查询返回结果,缓存没有时才执行方法。
@CacheEvict:使用该注解的方法执行后会清除缓存。
@CachePut:使用该注解的方法执行后会向缓存中放入一条数据。
2.使用缓存的步骤
2.1.在启动类或配置类上加@EnableCaching开启缓存
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class, args);
}
}
2.2.service实现类的查询方法上加@Cacheable
该注解的value表示缓存名称
该注解的key表示缓存下的key名称
按数据库举例的话value就像数据表,key就像数据表中的主键,这样快速定位到缓存拿到方法返回结果。
(key的值时SpEL表达式,所以写字符串时要加单引号,使用“#参数名”可根据参数值动态设置key。
如果不设置key的话spring会使用参数自动生成key)
例:根据编码和值查询,有缓存时优先从缓存中查询
@Cacheable(value = "cache1", key = "#code+':'+#value")
public String queryByCodeValue(String code, String value) {
return mapper.queryByCodeValue(code, value);
}
2.3.在service实现类的修改和删除方法上加@CacheEvict
该注解的value和key作用与@Cacheable相同
例:根据编码和值删除,方法执行完毕后根据缓存名(cache1)、编码和值将缓存中的对应数据清除
@CacheEvict(value = "cache1", key = "#code+':'+#value")
public void deleteByCodeValue(String code, String value) {
return mapper.deleteByCodeValue(code, value);
}
配置allEntries = true表示该缓存名下所有数据都要清除
例:根据ID删除,方法执行完毕后将该缓存名(cache1)下所有数据清除
@CacheEvict(value = "cache1", allEntries = true)
public void deleteById(String id) {
return mapper.deleteById(id);
}
3.spring整合redis进行缓存
3.1.需要引入依赖(启动器)
该依赖中包含了Jedis
Jedis就是集成了redis的一些命令操作,封装了redis的java客户端。提供了连接池管理。一般不直接使用jedis,而是在其上在封装一层,作为业务的使用。如果用spring的话,可以看看spring 封装的 redis Spring Data Redis
<!-- springboot整合redis的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.2.在spring配置文件中对缓存进行配置
spring:
cache:
# 缓存类型:使用redis
type: redis
redis:
# 缓存超时时间(毫秒ms)
time-to-live: 60000
# 是否缓存空值
cache-null-values: true
# 配置redis的ip、端口和密码
redis:
host: 127.0.0.1
port: 6379
password:
3.3.使用redis默认序列化方式时,方法返回对象必须实现序列化接口(Serializable)
使用redis进行缓存时,因为方法结果是序列化后保存到redis中,redis默认的序列化方式依赖于Serializable,
所以如果返回对象没实现序列化接口会在插入缓存时报序列化失败的错误。
注意:使用redis缓存自定义配置(见本章4redis缓存自定义配置),自定义序列化方式后,就可以不用实现序列化接口了。
4.redis缓存自定义配置
需要写一个redis配置类
(因为自己写配置类,所以@EnableCaching注解就不需要放在启动类上了,放在这个配置类上更好)
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysoft.base.common.CacheConstant;
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.connection.lettuce.LettuceConnectionFactory;
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;
import java.util.HashMap;
import java.util.Map;
/**
* redis缓存配置
*
* @author yudongbo 2021-06-03
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* json序列化方式
*/
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
return serializer;
}
/**
* 注入缓存配置cacheManager
* 此redis缓存配置会覆盖yml配置文件中的缓存配置
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config
// 设置key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
// 不缓存空值
.disableCachingNullValues()
// 设置缓存的默认过期时间 30分钟
.entryTtl(Duration.ofMinutes(30));
// 特殊缓存空间应用不同配置
Map<String, RedisCacheConfiguration> map = new HashMap<>(2);
// 数据字典 缓存60分钟
map.put(CacheConstant.SYS_DICT_CACHE, config.entryTtl(Duration.ofMinutes(60)));
// 自定义数据字典 缓存10分钟
map.put(CacheConstant.SYS_DICT_TABLE_CACHE, config.entryTtl(Duration.ofMinutes(10)));
// 使用自定义的缓存配置初始化一个
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
// 默认配置
.cacheDefaults(config)
// 特殊缓存配置
.withInitialCacheConfigurations(map)
// 事务
.transactionAware()
.build();
return cacheManager;
}
}
5.使用 RedisTemplate 操作redis
5.1.简单的操作
加载了依赖“spring-boot-starter-data-redis”后,spring容器中会帮我们创建一个bean(RedisTemplate)
5.1.1引入
@Autowired
private RedisTemplate redisTemplate;
5.1.2操作
// 向redis中插入一条数据
redisTemplate.opsForValue().set("key", "value", Duration.ofMinutes(30));
// 获取指定key的值
Object value = redisTemplate.opsForValue().get("key");
// 获取指定key的剩余时间(返回的是剩余秒数)
Long timestamp = redisTemplate.getExpire("key");
5.2.设置 RedisTemplate 序列化方式
之前的自定义设置序列化方式只适用于注解,并不会对 RedisTemplate 生效,所以要单独对 RedisTemplate 设置序列化方式。
在RedisConfig配置类中注入bean(redisTemplate)
完整版配置如下
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysoft.base.common.CacheConstant;
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.connection.lettuce.LettuceConnectionFactory;
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;
import java.util.HashMap;
import java.util.Map;
/**
* redis缓存配置
*
* @author yudongbo 2021-06-03
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* json序列化方式
*/
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
return serializer;
}
/**
* 注入缓存配置cacheManager
* 此redis缓存配置会覆盖yml配置文件中的缓存配置
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config
// 设置key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
// 不缓存空值
.disableCachingNullValues()
// 设置缓存的默认过期时间 30分钟
.entryTtl(Duration.ofMinutes(30));
// 特殊缓存空间应用不同配置
Map<String, RedisCacheConfiguration> map = new HashMap<>(2);
// 数据字典 缓存60分钟
map.put(CacheConstant.SYS_DICT_CACHE, config.entryTtl(Duration.ofMinutes(60)));
// 自定义数据字典 缓存10分钟
map.put(CacheConstant.SYS_DICT_TABLE_CACHE, config.entryTtl(Duration.ofMinutes(10)));
// 使用自定义的缓存配置初始化一个
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
// 默认配置
.cacheDefaults(config)
// 特殊缓存配置
.withInitialCacheConfigurations(map)
// 事务
.transactionAware()
.build();
return cacheManager;
}
/**
* 注入redisTemplate,覆盖依赖自带的bean
* 目的:进行自定义配置,如序列化方式
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
// 创建一个 RedisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 创建一个字符串序列化方式
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
// key 序列化方式(string)
redisTemplate.setKeySerializer(stringSerializer);
// value 序列化方式(json)
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
// Hash key 序列化方式(string)
redisTemplate.setHashKeySerializer(stringSerializer);
// Hash value 序列化方式(json)
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}