Spring 從 3.1 版本開始定義了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口來統一不一樣的緩存技術,並支持使用 JCache(JSR107)註解簡化咱們開發。html
Java Caching 定義了 5 個核心接口,分別是 CachingProvider、CacheManager、Cache、Entry 和Expiry。java
每次調用具備緩存功能的方法時,Spring 會檢查指定目標方法是否已經被調用過,若是有就直接從緩存中獲取方法調用後的結果,若是沒有就調用方法並緩存結果後返回,下次調用就直接從緩存中獲取。mysql
使用 Spring 緩存抽象時咱們須要關注如下兩點:web
Cache | 緩存接口,定義緩存操做。實現有:RedisCache、EhCacheCache、ConcurrentMapCache 等。 |
@CacheManager | 緩存管理器,管理各類緩存(Cache)組件。 |
@Cacheable | 主要針對方法配置,可以根據方法的請求參數對其結果進行緩存。 |
@CacheEvict | 清空緩存。 |
@CachePut | 保證方法被調用,又但願結果被緩存。 |
@EnableCaching | 開啓基於註解的緩存。 |
keyGenerator | 緩存數據時 key 的生成策略。 |
serialize | 緩存數據時 value 的序列化策略。 |
一、新建 SpringBoot 項目,引入以下場景啓動器:Cache、Web、Mysql、MyBatis。redis
二、初始化測試數據:spring
SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(40) DEFAULT NULL, `gender` int(11) DEFAULT NULL COMMENT '0:女 1:男', `birthday` date DEFAULT NULL, `address` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', '張三', '1', '1997-02-23', '北京'); INSERT INTO `user` VALUES ('2', '李四', '0', '1998-02-03', '武漢'); INSERT INTO `user` VALUES ('3', '王五', '1', '1996-06-04', '上海');
三、編寫與表對應的 JavaBean:sql
package com.springboot.bean; import java.util.Date; public class User { private Integer id; private String name; private Integer gender; private Date birthday; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", birthday=" + birthday + ", address='" + address + '\'' + '}'; } }
四、配置數據庫鏈接信息:數據庫
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.136:3306/springboot_cache driver-class-name: com.mysql.jdbc.Driver
五、編寫測試Mapper:apache
package com.springboot.mapper; import com.springboot.bean.User; import org.apache.ibatis.annotations.*; import java.util.List; public interface UserMappper { @Select("select * from user") public List<User> getAll(); @Select("select * from user where id=#{id}") public User getById(Integer id); @Delete("delete from user where id=#{id}") public void deleteById(Integer id); @Options(useGeneratedKeys = true,keyProperty = "id") @Insert("insert into user(name,gender,birthday,address) values(#{name},#{gender},#{birthday},#{address})") public void add(User user); @Update("update user set name=#{name},gender=#{gender},birthday=#{birthday},address=#{address} where id=#{id}") public void update(User user); @Select("select * from user where name=#{name}") public User getByName(String name); }
六、編寫 Service 並使用註解緩存功能:json
package com.springboot.service; import com.springboot.bean.User; import com.springboot.mapper.UserMappper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { @Autowired private UserMappper userMappper; @Cacheable(cacheNames = {"users"}) public List<User> getAll(){ System.out.println("UserService.getAll() 執行了"); return userMappper.getAll(); } }
七、配置 mapper 包掃描,開啓註解緩存:
package com.springboot; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching // 開啓註解緩存 @MapperScan("com.springboot.mapper") // mapper 包掃描 public class CacheTestApplication { public static void main(String[] args) { SpringApplication.run(CacheTestApplication.class, args); } }
八、編寫 Controller 並測試:
package com.springboot.controller; import com.springboot.bean.User; import com.springboot.mapper.UserMappper; import com.springboot.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/users") public List<User> getAll(){ return userService.getAll(); /* 首次訪問會輸出如下內容: UserService.getAll() 執行了 後續訪問不會輸出,說明實際上並無執行 userService.getAll() 方法,即未查詢數據庫從中獲取數據,而是從緩存中獲取 緩存成功 */ } }
CacheManager 是用來管理多個 Cache 組件的,對緩存的 CRUD 操做實際上仍是經過 Cache 組件,而每個 Cache 組件有本身惟一名稱。
主要針對方法配置,可以根據方法的請求參數對其結果進行緩存。
key 還能夠經過 SpEL 表達式進行取值。規則以下:
名字 | 位置 | 描述 | 實力 |
---|---|---|---|
methodName | root object | 當前被調用的方法名 | #root.methodName |
method | root object | 當前被調用的方法對象 | #root.method.name |
target | root object | 當前被調用的目標對象 | #root.target |
targetClass | root object | 當前被調用的目標對象類 | #root.targetClass |
args | root object | 當前被調用方法的參數列表 | #root.args[0] |
caches | root object | 當前被調用方法使用的緩存組件列表,如 @Cacheable(value={"cache1","cache2"}) 就有兩個緩存組件 | #root.caches[0].name |
argument name | evaluation context | 能夠直接使用 #參數名 來獲取對應參數值,也可使用 #p0 或 #a0 的形式,0 表明參數索引 | #id、#a0、#p0 |
result | evaluation context | 獲取方法執行後的返回值 | #result |
@Cacheable(cacheNames = {"users"}) public User getById(Integer id) { System.out.println("UserService.getById(" + id + ") 執行了"); return userMappper.getById(id); }
@GetMapping("/user/{id}") public User getById(@PathVariable Integer id) { return userService.getById(id); /* 首次訪問 http://localhost:8080/user/1 會輸出一下內容: UserService.getById(1) 執行了 後續訪問不會輸出,說明實際上並無執行 userService.getById(1) 方法,即未查詢數據庫從中獲取數據,而是從緩存中獲取 而若是訪問 http://localhost:8080/user/2 會輸出一下內容: UserService.getById(2) 執行了 後續訪問也不會輸出,即會自動根據參數的不一樣進行緩存 OK 緩存成功 */ }
主要針對方法配置,目標方法正常執行完成後將其結果更新到緩存。
參考 @Cacheable 註解屬性。
/* 注意:這裏要保證 key 與 getById 方法所使用 key 的生成後的值相同,不然會出現更新緩存後經過 getById 獲取數據依舊爲舊數據的狀況 */ @CachePut(cacheNames = {"users"}, key = "#user.id") public User update(User user) { System.out.println("UserService.update(" + user + ") 執行了"); userMappper.update(user); return user; }
@PutMapping("/user") public User update(User user){ return userService.update(user); /* 一、以 get 方式訪問 localhost:8080/user/1 會輸出一下內容: UserService.getById(1) 執行了 後續訪問不會輸出 二、以 put 方式訪問 localhost:8080/user,修改數據,輸出如下內容: UserService.update(User{id=1, name='張三new', gender=0, birthday=Wed Apr 03 00:00:00 CST 1996, address='深圳'}) 執行了 每次訪問都會輸出 三、以 get 方式訪問 localhost:8080/user/1 不會輸出內容,返回數據是更新後的數據 */ }
主要針對方法配置,默認在目標方法正常執行完成後清除緩存。
更多可參考 @Cacheable 註解屬性。
/* 刪除緩存,key 能夠不指定,由於 key 默認就是以 id 爲基礎生成的 */ @CacheEvict(cacheNames = {"users"}, key = "#id") public void delete(Integer id) { System.out.println("UserService.delete(" + id + ") 執行了"); }
@DeleteMapping("/user/{id}") public void delete(@PathVariable Integer id){ userService.delete(id); /* 一、以 get 方式訪問 localhost:8080/user/1 會輸出如下內容 UserService.getById(1) 執行了 後續訪問不會輸出 二、以 delete 方式訪問 localhost:8080/user/1 會輸出如下內容 UserService.delete(1) 執行了 每次訪問都會輸出 三、以 get 方式再次訪問 localhost:8080/user/1 會輸出如下內容 UserService.getById(1) 執行了 即緩存被刪除了從新執行方法獲取了數據 */ }
主要針對方法配置,爲 @Cacheable、@CachePut、CacheEvict 的組合註解,經常使用於定製較複雜的緩存策略。
@Caching( cacheable = { // 以 name 生成 key 進行緩存 @Cacheable(value = "users", key = "#name") }, put = { // 以 id 生成 key ,且執行結果不爲空時更新緩存 @CachePut(value = "users", key = "#result.id", condition = "#result != null") }, evict = { // name 爲 deleteAll 清除全部緩存 @CacheEvict(value = "users", condition = "#name=='deleteAll'", allEntries = true) } ) public User getByName(String name) { System.out.println("UserService.getByName(" + name + ") 執行了"); return userMappper.getByName(name); }
@GetMapping("/user/name/{name}") public User getByName(@PathVariable String name){ return userService.getByName(name); /* 一、以 get 方式請求 localhost:8080/user/name/李四,輸出如下內容: UserService.getByName(李四) 執行了 每次訪問都會輸出,由於 @CachePut 須要執行結果更新緩存 二、以 get 方式請求 localhost:8080/user/2,沒有輸出內容,由於經過 [1] 已經完成了 id 爲 2 的緩存,直接從緩存中取出結果返回了 三、以 get 方式請求 localhost:8080/user/name/deleteAll,輸出如下內容: UserService.getByName(deleteAll) 執行了 每次訪問都會輸出,由於 @CachePut 須要執行結果更新緩存 四、以 get 方式請求 localhost:8080/user/2,輸出如下內容: UserService.getById(2) 執行了 後續訪問不會輸出,此次輸出的緣由是經過 [3] 清楚了全部緩存 */ }
一般用來標註在類上,用來定義公共緩存配置。
具備以下屬性:cachenames、keyGenerator、cacheManager、cacheResolver,可參考 @Cacheable 註解屬性。
package com.springboot.service; import com.springboot.bean.User; import com.springboot.mapper.UserMappper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.*; import org.springframework.stereotype.Service; /** * 由於下面方法都是使用同一個 Cache 組件,因此能夠在類上一次性指定全部方法使用的 Cache 組件名稱 */ @CacheConfig(cacheNames = {"users"} ) @Service public class UserService { @Autowired private UserMappper userMappper; @Cacheable(keyGenerator = "myKeyGenerator") public User getById(Integer id) { System.out.println("UserService.getById(" + id + ") 執行了"); return userMappper.getById(id); } @CachePut(key = "#user.id") public User update(User user) { System.out.println("UserService.update(" + user + ") 執行了"); userMappper.update(user); return user; } @CacheEvict(key = "#id") public void delete(Integer id) { System.out.println("UserService.delete(" + id + ") 執行了"); } @Caching( cacheable = { @Cacheable(key = "#name") }, put = { @CachePut(key = "#result.id", condition = "#result != null") }, evict = { @CacheEvict(condition = "#name=='deleteAll'", allEntries = true) } ) public User getByName(String name) { System.out.println("UserService.getByName(" + name + ") 執行了"); return userMappper.getByName(name); } }
依舊是從自動配置類入手以 @Cacheable 註解執行流程進行分析:
1 package org.springframework.boot.autoconfigure.cache; 2 3 @Configuration 4 @ConditionalOnClass(CacheManager.class) 5 @ConditionalOnBean(CacheAspectSupport.class) 6 @ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver") 7 @EnableConfigurationProperties(CacheProperties.class) 8 @AutoConfigureBefore(HibernateJpaAutoConfiguration.class) 9 @AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, 10 RedisAutoConfiguration.class }) 11 @Import(CacheConfigurationImportSelector.class) 12 public class CacheAutoConfiguration { 13 14 // 註冊緩存管理器定製器 15 @Bean 16 @ConditionalOnMissingBean 17 public CacheManagerCustomizers cacheManagerCustomizers( 18 ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) { 19 return new CacheManagerCustomizers(customizers.getIfAvailable()); 20 } 21 22 @Bean 23 public CacheManagerValidator cacheAutoConfigurationValidator( 24 CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { 25 return new CacheManagerValidator(cacheProperties, cacheManager); 26 } 27 28 @Configuration 29 @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class) 30 @ConditionalOnBean(AbstractEntityManagerFactoryBean.class) 31 protected static class CacheManagerJpaDependencyConfiguration 32 extends EntityManagerFactoryDependsOnPostProcessor { 33 34 public CacheManagerJpaDependencyConfiguration() { 35 super("cacheManager"); 36 } 37 38 } 39 40 static class CacheManagerValidator implements InitializingBean { 41 42 private final CacheProperties cacheProperties; 43 44 private final ObjectProvider<CacheManager> cacheManager; 45 46 CacheManagerValidator(CacheProperties cacheProperties, 47 ObjectProvider<CacheManager> cacheManager) { 48 this.cacheProperties = cacheProperties; 49 this.cacheManager = cacheManager; 50 } 51 52 @Override 53 public void afterPropertiesSet() { 54 Assert.notNull(this.cacheManager.getIfAvailable(), 55 "No cache manager could " 56 + "be auto-configured, check your configuration (caching " 57 + "type is '" + this.cacheProperties.getType() + "')"); 58 } 59 60 } 61 62 static class CacheConfigurationImportSelector implements ImportSelector { 63 64 @Override 65 public String[] selectImports(AnnotationMetadata importingClassMetadata) { 66 CacheType[] types = CacheType.values(); 67 String[] imports = new String[types.length]; 68 for (int i = 0; i < types.length; i++) { 69 imports[i] = CacheConfigurations.getConfigurationClass(types[i]); 70 } 71 return imports; 72 } 73 } 74 }
看到第 11 行,該行是經過 @Import(CacheConfigurationImportSelector.class) 導入緩存配置類, CacheConfigurationImportSelector 爲自動配置類中的內部類,對應的導入方法爲 65 行 selectImports 方法。經過調試會發現它實際導入了以下配置類:
org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
那麼實際生效的是哪一個配置類呢?能夠在配置文件中開啓 debug 模式,接着在控制檯中就能夠看到:
SimpleCacheConfiguration matched: - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition) - @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)
即生效的配置類爲 org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration 。查看該配置類:
1 package org.springframework.boot.autoconfigure.cache; 2 3 @Configuration 4 @ConditionalOnMissingBean(CacheManager.class) 5 @Conditional(CacheCondition.class) 6 class SimpleCacheConfiguration { 7 8 private final CacheProperties cacheProperties; 9 10 private final CacheManagerCustomizers customizerInvoker; 11 12 SimpleCacheConfiguration(CacheProperties cacheProperties, 13 CacheManagerCustomizers customizerInvoker) { 14 this.cacheProperties = cacheProperties; 15 this.customizerInvoker = customizerInvoker; 16 } 17 18 @Bean 19 public ConcurrentMapCacheManager cacheManager() { 20 ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); 21 List<String> cacheNames = this.cacheProperties.getCacheNames(); 22 if (!cacheNames.isEmpty()) { 23 cacheManager.setCacheNames(cacheNames); 24 } 25 return this.customizerInvoker.customize(cacheManager); 26 } 27 }
在第 19 行能夠看到往 IoC 容器中註冊了一個 org.springframework.cache.concurrent.ConcurrentMapCacheManager ,該類實現了 org.springframework.cache.CacheManager 接口,可使用它的 getCache(String) 方法來獲取緩存組件:
1 public Cache getCache(String name) { 2 // this.cacheMap : private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16); 3 Cache cache = (Cache)this.cacheMap.get(name); 4 if (cache == null && this.dynamic) { 5 synchronized(this.cacheMap) { 6 // 從 ConcurrentMap 對象中獲取 Cache 組件 7 cache = (Cache)this.cacheMap.get(name); 8 if (cache == null) { // 若是獲取的 Cache 組件爲空 9 // 新建立一個 ConcurrentMapCache 組件 10 cache = this.createConcurrentMapCache(name); 11 // 將其放入 ConcurrentMap 對象中 12 this.cacheMap.put(name, cache); 13 } 14 } 15 } 16 return cache; 17 }
能夠看到該方法的做用實際上就是用來獲取一個 ConcurrentMapCache 類型的 Cache 組件,而咱們經過 Cache 組件獲取數據時是經過 get 方法,最終是經過該類的 get 方法調用 lookup(key) 方法:
1 @Override 2 protected Object lookup(Object key) { 3 // this.store : private final ConcurrentMap<Object, Object> store; 4 return this.store.get(key); 5 }
而這個 key 默認是使用 org.springframework.cache.interceptor.SimpleKeyGenerator#generateKey 生成的:
1 package org.springframework.cache.interceptor; 2 3 import java.lang.reflect.Method; 4 5 public class SimpleKeyGenerator implements KeyGenerator { 6 7 @Override 8 public Object generate(Object target, Method method, Object... params) { 9 return generateKey(params); 10 } 11 12 public static Object generateKey(Object... params) { 13 if (params.length == 0) { 14 //SimpleKey.EMPTY : public static final SimpleKey EMPTY = new SimpleKey(); 15 return SimpleKey.EMPTY; 16 } 17 if (params.length == 1) { 18 Object param = params[0]; 19 if (param != null && !param.getClass().isArray()) { 20 return param; 21 } 22 } 23 return new SimpleKey(params); 24 } 25 }
能夠看到它的生成規則以下:
即 ConcurrentMapCache 組件也是將實際緩存數據存放在 ConcurrentMap 對象中。
ConcurrentMapCache 類實現了 Cache 接口,Cache 接口中定義了 get 方法用來獲取數據、put 方法用來存放數據、evict 方法用來刪除指定數據、clear 方法用來清空全部數據。
上述運行流程以下:
默認狀況下:
參考【Linux 下源碼安裝 Redis】或【Docker 安裝鏈接 Redis】。
一、在上面簡單使用示例項目的基礎上再引入 redis 緩存的場景啓動器:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
二、配置 redis:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.136:3306/springboot_cache driver-class-name: com.mysql.jdbc.Driver redis: # 指定 redis 主機地址 host: 192.168.202.136
三、測試:
訪問 localhost:8080/user/1,而後查看 Redis:
會發現 id 爲 1 的用戶已經被緩存到 redis。
package com.springboot; import com.springboot.bean.User; import com.springboot.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class RedisTests { @Autowired private RedisTemplate redisTemplate; // k-v 都爲字符串 @Autowired private StringRedisTemplate stringRedisTemplate; // k-v 都爲 Object @Autowired private UserService userService; // 操做字符串 @Test public void testString(){ stringRedisTemplate.opsForValue().set("msg","hello 張三"); String msg = stringRedisTemplate.opsForValue().get("msg"); System.out.println(msg); /* hello 張三 */ } // 操做對象 // 注意:序列化類型須要實現 Serializable 接口 @Test public void testObject(){ User user = userService.getById(2); redisTemplate.opsForValue().set("id_2", user); Object user2 = redisTemplate.opsForValue().get("id_2"); System.out.println(user2); /* User{id=2, name='李四', gender=0, birthday=Tue Feb 03 00:00:00 CST 1998, address='武漢'} */ } /* 還可經過以下幾種方式操做列表、集合、有序集合、哈希 RedisTemplate.opsForList() RedisTemplate.opsForSet() RedisTemplate.opsForZSet() RedisTemplate.opsForHash() */ }
通過上述測試咱們查看 redis 中數據會發現 redis 中保存的是 SpringBoot 以默認方式序列化後的數據,若是咱們想要以 json 方式序列化保存數據到 redis 咱們該怎麼作呢?
咱們使用的 RedisTemplate 和 StringRedisTemplate bean 都是在 redis 的自動配置類中註冊的,查看:
1 @Configuration 2 protected static class RedisConfiguration { 3 4 @Bean 5 @ConditionalOnMissingBean(name = "redisTemplate") 6 public RedisTemplate<Object, Object> redisTemplate( 7 RedisConnectionFactory redisConnectionFactory) 8 throws UnknownHostException { 9 RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>(); 10 template.setConnectionFactory(redisConnectionFactory); 11 return template; 12 } 13 14 @Bean 15 @ConditionalOnMissingBean(StringRedisTemplate.class) 16 public StringRedisTemplate stringRedisTemplate( 17 RedisConnectionFactory redisConnectionFactory) 18 throws UnknownHostException { 19 StringRedisTemplate template = new StringRedisTemplate(); 20 template.setConnectionFactory(redisConnectionFactory); 21 return template; 22 } 23 24 }
每一個 RedisTemplate 對象均可定製本身的序列化器,查看源碼會發現它默認使用的序列化器爲 org.springframework.data.redis.serializer.JdkSerializationRedisSerializer 。咱們只須要修改它默認的序列化器便可:
package com.springboot.config; import com.springboot.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; @Configuration public class CacheConfig { @Bean public RedisTemplate<Object, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<Object, User> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<User>(User.class); template.setDefaultSerializer(serializer); return template; } }
package com.springboot; import com.springboot.bean.Dept; import com.springboot.bean.User; import com.springboot.service.DeptService; import com.springboot.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class RedisTests { @Autowired private UserService userService; @Autowired private RedisTemplate userRedisTemplate; @Test public void test(){ User user = userService.getById(2); userRedisTemplate.opsForValue().set("id_2", user); Object user2 = userRedisTemplate.opsForValue().get("id_2"); System.out.println(user2); /* User{id=2, name='李四', gender=0, birthday=Tue Feb 03 00:00:00 CST 1998, address='武漢'} */ } }
上面說的是經過 RedisTemplate 操做保存 json 數據到 redis,若是要使用註解方式該怎麼作呢?
一旦咱們引入了 redis 緩存的場景啓動器,那麼默認生效的緩存配置類就變成了 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration :
1 package org.springframework.boot.autoconfigure.cache; 2 3 @Configuration 4 @AutoConfigureAfter(RedisAutoConfiguration.class) 5 @ConditionalOnBean(RedisTemplate.class) 6 @ConditionalOnMissingBean(CacheManager.class) 7 @Conditional(CacheCondition.class) 8 class RedisCacheConfiguration { 9 10 private final CacheProperties cacheProperties; 11 12 private final CacheManagerCustomizers customizerInvoker; 13 14 RedisCacheConfiguration(CacheProperties cacheProperties, 15 CacheManagerCustomizers customizerInvoker) { 16 this.cacheProperties = cacheProperties; 17 this.customizerInvoker = customizerInvoker; 18 } 19 20 @Bean 21 public RedisCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) { 22 RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); 23 cacheManager.setUsePrefix(true); 24 List<String> cacheNames = this.cacheProperties.getCacheNames(); 25 if (!cacheNames.isEmpty()) { 26 cacheManager.setCacheNames(cacheNames); 27 } 28 return this.customizerInvoker.customize(cacheManager); 29 } 30 31 }
緩存管理器也更換爲第 21 行的 RedisCacheManager,RedisCacheManager 幫咱們建立 RedisCache 來做爲緩存組件,而 RedisCache 組件就是經過操做 Redis 緩存數據的。
在第 22 行也能夠看到,建立 RedisCacheManager 實例時經過構造方法傳入的 RedisTemplate,因此咱們只須要本身定義一個 RedisCacheManager,讓其 RedisTemplate 是使用 json 序列化器便可:
package com.springboot.config; import com.springboot.bean.Dept; import com.springboot.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; @Configuration public class CacheConfig { @Bean public RedisTemplate<Object, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<Object, User> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<User>(User.class); template.setDefaultSerializer(serializer); return template; } @Bean public RedisTemplate<Object, Dept> deptRedisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<Object, Dept> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<Dept> serializer = new Jackson2JsonRedisSerializer<Dept>(Dept.class); template.setDefaultSerializer(serializer); return template; } @Primary // 默認 @Bean public RedisCacheManager userCacheManager(RedisTemplate<Object, User> redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); // key 使用 cacheName 做爲前綴 cacheManager.setUsePrefix(true); return cacheManager; } @Bean public RedisCacheManager deptCacheManager(RedisTemplate<Object, Dept> redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); // key 使用 cacheName 做爲前綴 cacheManager.setUsePrefix(true); return cacheManager; } }
而後再使用註解時指定要使用的緩存管理器便可。
還能夠經過手動編碼方式獲取到緩存管理器對指定緩存組件進行操做:
package com.springboot.service; import com.springboot.bean.Dept; import com.springboot.bean.User; import com.springboot.mapper.DeptMappper; import com.springboot.mapper.UserMappper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.annotation.*; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.stereotype.Service; @Service @CacheConfig(cacheManager = "deptCacheManager") public class DeptService { @Autowired private DeptMappper deptMappper; @Autowired private RedisCacheManager deptCacheManager; public Dept getById(Integer id) { System.out.println("DeptService.getById(" + id + ") 執行了"); Dept dept = deptMappper.getById(id); // 獲取到緩存組件 Cache cache = deptCacheManager.getCache("dept"); cache.put(id, dept); return dept; } }