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; } }