①,一個linux虛擬機,安裝好docker,再安裝redis鏡像,而後啓動redis容器java
#下載redis鏡像 docker pull registry.docker-cn.com/library/redis #-d 後臺運行 -p 暴露端口 --name 起名 最後帶上鏡像名便可 docker run -d -p 6379:6379 --name myredis bfcb1f6df2db #啓動redis 容器 docker start myredis
②,身爲程序員linux不可不玩,而docker就是一個強有力的工具mysql
③,springboot版本2.0.2.RELEASElinux
④,pom依賴程序員
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
import java.net.UnknownHostException; import java.time.Duration; 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.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; @EnableCaching @Configuration public class CacheConfig { @Bean public RedisCacheConfiguration redisCacheConfiguration() { RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>( Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 將類型序列化到屬性json字符串中 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); configuration = configuration .serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .entryTtl(Duration.ofDays(30)); return configuration; } @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>( Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 將類型序列化到屬性json字符串中 jackson2JsonRedisSerializer.setObjectMapper(om); template.setKeySerializer(jackson2JsonRedisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } }
spring.redis.host=192.168.43.28 spring.redis.port=6379
①,注意只要配置瞭如上信息,web
springboot的默認緩存就會從SimpleCacheConfiguration 切換成RedisCacheConfiguration
②,原理以下redis
@Configuration //主要是這裏,當沒有CacheManager 時生效 @ConditionalOnMissingBean(CacheManager.class) @Conditional(CacheCondition.class) class SimpleCacheConfiguration {
③,springboot緩存的加載循序以下spring
//CacheAutoConfiguration 的配置以下 static class CacheConfigurationImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for (int i = 0; i < types.length; i++) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } }
由於redis在simple的前面,一旦RedisCacheConfiguration 生效就會配置RedisCacheManager,而後simple就不注入ioc容器了sql
//使用RedisCacheManager 用於建立cache @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) { RedisCacheManagerBuilder builder = RedisCacheManager .builder(redisConnectionFactory) .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader())); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet<>(cacheNames)); } return this.customizerInvoker.customize(builder.build()); }
//RedisCacheManager 建立cache的方法 protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) { return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig); }
//①,根據必定的策略生成緩存的key protected String createCacheKey(Object key) { String convertedKey = convertKey(key); if (!cacheConfig.usePrefix()) { return convertedKey; } return prefixCacheKey(convertedKey); } //②,先嚐試獲取緩存 public byte[] get(String name, byte[] key) { Assert.notNull(name, "Name must not be null!"); Assert.notNull(key, "Key must not be null!"); return execute(name, connection -> connection.get(key)); } //③,獲取不到就執行目標方法,並將返回結果放入緩存 public void put(Object key, @Nullable Object value) { Object cacheValue = preProcessCacheValue(value); if (!isAllowNullValues() && cacheValue == null) { throw new IllegalArgumentException(String.format( "Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.", name)); } cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl()); }
//①,根據必定的策略生成緩存的key protected String createCacheKey(Object key) { String convertedKey = convertKey(key); if (!cacheConfig.usePrefix()) { return convertedKey; } return prefixCacheKey(convertedKey); } //②,嘗試根據能夠獲取對應的緩存 @Override public byte[] get(String name, byte[] key) { Assert.notNull(name, "Name must not be null!"); Assert.notNull(key, "Key must not be null!"); return execute(name, connection -> connection.get(key)); } //③,獲取成功則不執行目標方法,直接將緩存返回
①,注意,緩存的數據要帶有緩存的類型,不然redis緩存反序列化成對應pojo會失敗,只要按上面的CacheConfig配置好RedisCacheConfiguration 便可docker
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class RedisController { @Autowired private RedisTemplate<Object, Object> redisTemplate; @RequestMapping("put") @ResponseBody public String put(String key) { ValueOperations<Object, Object> opsForValue = redisTemplate.opsForValue(); Object val = opsForValue.get(key); if(val==null) { String value="小純"+key; opsForValue.set(key, value); return "key:"+key+",val"+value +",加入緩存"; } return "key:"+key+",val"+val +",已在緩存中存在"; } }