Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
1、安装redis;
2、引入redis的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3、配置redis
这一步也很简单只需要在配置文件中指定redis的主机地址即可
spring.redis.host=118.24.44.169
4.原理分析
当我们在项目中配置好了redis后,RedisAutoConfiguration配置类就会起作用,下面是配置类的内容:
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
可以看到配置类为我们向容器中加入了零个redisTemplate(redisTemplate和stringRedisTemplate),这个就是我们用来操作redis的工具。(类似于JpaTemplate)。我们使用redis时@Autowired这两个template即可使用redis,例如:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot01CacheApplicationTests {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
StringRedisTemplate stringRedisTemplate; //操作k-v都是字符串的
@Autowired
RedisTemplate redisTemplate; //k-v都是对象的
@Autowired
RedisTemplate<Object, Employee> empRedisTemplate;
/**
* Redis常见的五大数据类型
* String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
* stringRedisTemplate.opsForValue()[String(字符串)]
* stringRedisTemplate.opsForList()[List(列表)]
* stringRedisTemplate.opsForSet()[Set(集合)]
* stringRedisTemplate.opsForHash()[Hash(散列)]
* stringRedisTemplate.opsForZSet()[ZSet(有序集合)]
*/
@Test
public void test01(){
给redis中保存数据
stringRedisTemplate.opsForValue().append("msg","hello");
String msg = stringRedisTemplate.opsForValue().get("msg");
System.out.println(msg);
stringRedisTemplate.opsForList().leftPush("mylist","1");
stringRedisTemplate.opsForList().leftPush("mylist","2");
}
//测试保存对象
@Test
public void test02(){
Employee empById = employeeMapper.getEmpById(1);
//默认如果保存对象,使用jdk序列化机制,序列化后的数据保存到redis中
redisTemplate.opsForValue().set("emp-01",empById);
}
@Test
public void contextLoads() {
Employee empById = employeeMapper.getEmpById(1);
System.out.println(empById);
}
}
在上面的测测试中我们会发现,如果保存对象,默认使用jdk序列化机制,序列化后的数据保存到redis中,这会导致redis数据库中存入的值很不友好。
5.定义序列化机制
要让存入的结果友好我们有两种方式:
(1)自己将对象转为json
(2)redisTemplate默认的序列化规则;改变默认的序列化规则;
其实我们来看看RedisTemplate就会发现其中定义了很多Serializer(序列化器)
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private ClassLoader classLoader;
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
//这里可以看到当defaultSerializer 为null时,默认使用JdkSerializationRedisSerializer(jdk的序列化器)
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
if (this.enableDefaultSerializer) {
if (this.keySerializer == null) {
this.keySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.valueSerializer == null) {
this.valueSerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashValueSerializer == null) {
this.hashValueSerializer = this.defaultSerializer;
defaultUsed = true;
}
}
if (this.enableDefaultSerializer && defaultUsed) {
Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
}
if (this.scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor(this);
}
this.initialized = true;
}
因为默认使用JdkSerializationRedisSerializer,所以我们可以设置DefaultSerializer为自己想要的序列化器,就像RedisAutoConfiguration 中一样在加RedisTemplate时设置defaultSerializer,代码如下:
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>();
template.setConnectionFactory(redisConnectionFactory);
//这里使用Jackson2JsonRedisSerializer。
Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
template.setDefaultSerializer(ser);
return template;
}
}
这样我们在使用RedisTemplate时就会按照自定义的序列化机制来存入数据了。
6.缓存原理
在我的SpringBoot缓存配置原理博文中介绍了缓存实际上是通过cacheManager来实现的,当容器中没有cacheManager时会默认配置SimpleCacheConfiguration。因为我们引入了Redis,所以容器会自动匹配到RedisCacheConfiguration.这是为什么呢?我们先来看看RedisCacheConfiguration:
@ConditionalOnClass({RedisConnectionFactory.class})
@AutoConfigureAfter({RedisAutoConfiguration.class})
@ConditionalOnBean({RedisConnectionFactory.class})
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class RedisCacheConfiguration {
RedisCacheConfiguration() {
}
@Bean
RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers, RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(this.determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet(cacheNames));
}
...
可以看出,容器根据@Conditional注解匹配到RedisCacheConfiguration 后,RedisCacheConfiguration 会给我们在容器中@Bean一个
cacheManager(RedisCacheManager ) ,这就使SimpleCacheManager 的匹配条件失败。(匹配过程是有顺序的)。
下面就可以来看看RedisCacheManager是如何帮我们缓存的:
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
//加载缓存
protected Collection<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList();
Iterator var2 = this.initialCacheConfiguration.entrySet().iterator();
while(var2.hasNext()) {
Entry<String, RedisCacheConfiguration> entry = (Entry)var2.next();
caches.add(this.createRedisCache((String)entry.getKey(), (RedisCacheConfiguration)entry.getValue()));
}
return caches;
}
//创建缓存
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new RedisCache(name, this.cacheWriter, cacheConfig != null ? cacheConfig : this.defaultCacheConfig);
}
可以看到RedisCacheManager 会给我们创建RedisCache 作为缓存组件,RedisCache通过操作redis来存取数据。