[关闭]
@javazjm 2017-11-01T12:45:23.000000Z 字数 11885 阅读 2513

Springboot系列学习十三:数据缓存

Springboot Cache ehcache redis


缓存基础

Springboot 使用缓存很简单,只需遵循以下几步就可以。

1. 引入依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>

2. 开启缓存

在启动类上加注解@enableCaching,启用缓存

3. 使用缓存注解

在需要缓存的方法上,使用注解

  1. 1.@Cacheable(cacheNames = "car", key = "#name")
  2. 将方法的返回值 保存 在缓存“car”中,键由key指定,值是方法的返回值
  3. 2.@CachePut(cacheNames = "car", key = "#car.name")
  4. 使用方法的返回值 更新 缓存“car”中,键为key的值
  5. 3.@CacheEvict(cacheNames = "car", allEntries = true)
  6. 根据keycondition删除缓存,如果指定allEntriestrue,则删除缓存中所有的对象

4. 创建实体并序列化

5. 测试

说明
这里并没有明确指定缓存提供者,但是Springboot会按照以下顺序寻找提供者:

31.1.1. Generic
31.1.2. JCache (JSR-107)
31.1.3. EhCache 2.x
31.1.4. Hazelcast
31.1.5. Infinispan
31.1.6. Couchbase
31.1.7. Redis
31.1.8. Caffeine
31.1.9. Simple

若没有查找到前9类缓存的cacheManager,则会使用最后一个simple缓存,也就是在内存中使用ConcurrentHashMap实现缓存。spring官方建议生产环境中勿使用simple缓存。
本实例就是使用的simple缓存。

Redis Cache

redis缓存

注意:

1.
使用RedisTemplate,结果发现redis里查找不到对应的key-value键值对,原因是spring-data-redis的RedisTemplate V>模板类在操作redis时默认使用JdkSerializationRedisSerializer来进行序列化.

2.
SpringBoot提供了对Redis的自动配置功能,在RedisAutoConfiguration中默认为我们配置了JedisConnectionFactory(客户端连接)、RedisTemplate以及StringRedisTemplate(数据操作模板),其中StringRedisTemplate模板只针对键值对都是字符型的数据进行操作

1、引入依赖

Spring会找到redisCacheManager,使用redis作为缓存。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>

2、application.properties中配置redis属性

  1. # REDIS (RedisProperties)
  2. # Redis数据库索引(默认为0)
  3. spring.redis.database=0
  4. # Redis服务器地址
  5. spring.redis.host=127.0.0.1
  6. # Redis服务器连接端口
  7. spring.redis.port=6379
  8. # Redis服务器连接密码(默认为空)
  9. spring.redis.password=
  10. # 连接池最大连接数(使用负值表示没有限制)
  11. spring.redis.pool.max-active=8
  12. # 连接池最大阻塞等待时间(使用负值表示没有限制)
  13. spring.redis.pool.max-wait=-1
  14. # 连接池中的最大空闲连接
  15. spring.redis.pool.max-idle=8
  16. # 连接池中的最小空闲连接
  17. spring.redis.pool.min-idle=0
  18. # 连接超时时间(毫秒)
  19. spring.redis.timeout=0

3.新建实体并实现序列化

  1. @Entity
  2. @Table(name = "userInfo")
  3. public class User implements Serializable {
  4. @Id
  5. @GeneratedValue
  6. private Long id;
  7. @Column(nullable = false)
  8. private String userName;
  9. @Column(nullable = false)
  10. private String passWord;
  11. @Column(nullable = false)
  12. private String email;
  13. @Column(nullable = false, unique = true)
  14. private String nickName;
  15. @Column(nullable = false)
  16. private Date regTime;
  17. // setter getter...
  18. }

4. 实现类

  1. /**
  2. * @Cacheable 应用到读取数据的方法上,先从缓存中读取,如果没有再从DB获取数据,然后把数据添加到缓存中
  3. * @param id
  4. * keyGenerator 和 key 不能同时存在
  5. * 否则:Both 'key' and 'keyGenerator' attributes have been set
  6. * @return
  7. */
  8. @Cacheable(value = "user", key = "'user'+ '_'+ #id", unless = "#result eq null")
  9. public User getUserById(Long id) {
  10. return entityManager.find(User.class, id);
  11. }
  12. @Cacheable(value = "user", key = "#root.methodName + '_users'", unless = "#result eq null ")
  13. public List<User> getUsers(){
  14. return userRepository.findAll();
  15. }
  16. /**
  17. * 使用自定义注解
  18. * @param user
  19. * @return
  20. */
  21. @UserSaveCache
  22. public User addUser3(User user) {
  23. return userRepository.save(user);
  24. }

自定义注解

  1. @Caching(put = {
  2. @CachePut(value = "user", key = "'user' + #result.id"),
  3. @CachePut(value = "user", key = "'user' + #result.nickName"),
  4. @CachePut(value = "user", key = "'user' + #result.email"),
  5. })
  6. @Target({ElementType.METHOD, ElementType.TYPE})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Inherited
  9. public @interface UserSaveCache {
  10. }

5. config配置文件

  1. @Configuration
  2. @EnableCaching
  3. public class RedisConfig extends CachingConfigurerSupport {
  4. /**
  5. * key 生成策略
  6. * @return
  7. */
  8. @Bean
  9. public KeyGenerator keyGenerator(){
  10. return new KeyGenerator() {
  11. @Override
  12. public Object generate(Object o, Method method, Object... objects) {
  13. StringBuilder sb = new StringBuilder();
  14. sb.append(o.getClass().getName());
  15. sb.append(method.getName());
  16. for (Object obj : objects) {
  17. sb.append(obj.toString());
  18. }
  19. return sb.toString();
  20. }
  21. };
  22. }
  23. /**
  24. * 如果不做额外配置可以不需要,springboot会根据所使用的缓存,自动配置缓存管理器
  25. * @param redisTemplate
  26. * @return
  27. */
  28. @SuppressWarnings("rawtypes")
  29. @Bean
  30. public CacheManager cacheManager(RedisTemplate redisTemplate) {
  31. RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
  32. //设置缓存过期时间
  33. long expireTime = 600;
  34. rcm.setDefaultExpiration(expireTime);//秒 管用
  35. // System.out.println("通用过期时间:" + expireTime);
  36. return rcm;
  37. }
  38. /**
  39. * 连接工厂 默认自动配置JedisConnectionFactory(客户端连接)
  40. * @return
  41. */
  42. @Bean
  43. JedisConnectionFactory jedisConnectionFactory() {
  44. return new JedisConnectionFactory();
  45. }
  46. @Bean
  47. public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
  48. RedisTemplate<String, String> template = new RedisTemplate<>();
  49. RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
  50. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  51. ObjectMapper om = new ObjectMapper();
  52. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  53. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  54. jackson2JsonRedisSerializer.setObjectMapper(om);
  55. template.setConnectionFactory(factory);
  56. // 开启事务支持
  57. template.setEnableTransactionSupport(true);
  58. // template.setEnableDefaultSerializer(false); // 将任何序列化器设置为null,并使用原始字节数组来使用RedisTemplate
  59. //key序列化方式 stringRedisSerializer
  60. template.setKeySerializer(stringRedisSerializer);
  61. template.setHashKeySerializer(stringRedisSerializer);
  62. //value序列化 jackson2JsonRedisSerializer
  63. template.setValueSerializer(jackson2JsonRedisSerializer);
  64. // template.setValueSerializer(new RedisObjectSerializer()); // value是byte字节 \xAC\xED\x00\x05t\x00\x09\xE5\xBC\xA0\xE4\xBA\x8C\xE7\xA3\x8A
  65. template.setHashValueSerializer(jackson2JsonRedisSerializer);
  66. template.afterPropertiesSet();
  67. return template;
  68. }
  69. }

6.单元测试

  1. @Autowired
  2. private StringRedisTemplate stringRedisTemplate; // 只能操作字符串
  3. @Autowired
  4. private RedisTemplate redisTemplate; // 通用性广
  5. @Autowired
  6. private UserService userService;
  7. @Test
  8. public void testSer(){
  9. User user = new User("张无忌", "123456", "itzjm@qq.com", "zhangwuji", new Date());
  10. User user1 = userService.addUser3(user);
  11. System.out.println(user1.getId()+"=======");// 16
  12. User userById = userService.getUserById(1L);
  13. System.out.println(userById);
  14. }

可以将redis的操作封装到一个类中RedisCacheService,直接在所需要的类中引入即可。

代码详见:https://github.com/JavaerZJM/springboot-workspace

参考:

EhCache

ehcache 3.x

根据官网:http://www.ehcache.org/blog/2016/05/18/ehcache3_jsr107_spring.html

1. 导入依赖

  1. <!-- ehcache 3.x -->
  2. <dependency>
  3. <groupId>org.ehcache</groupId>
  4. <artifactId>ehcache</artifactId>
  5. <version>3.3.1</version>
  6. </dependency>
  7. <!-- JSR107 API -->
  8. <dependency>
  9. <groupId>javax.cache</groupId>
  10. <artifactId>cache-api</artifactId>
  11. </dependency>
  12. <!-- springboot cache -->
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-cache</artifactId>
  16. </dependency>

2. 设置spring.cache.jcache

需要在application.properties文件中配置:包括类路径和ehcache。xml文件

  1. # ehcache配置
  2. spring.cache.jcache.config=classpath:ehcache.xml

3. 方法中使用缓存注解

  1. @Cacheable(value = "studentCache", key = "'student_'+#id")
  2. @Override
  3. public Student findById(Long id) {
  4. return studentDao.findOne(id);
  5. }

4. 配置ehcache.xml文件

  1. <config
  2. xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
  3. xmlns='http://www.ehcache.org/v3'
  4. xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>
  5. <service>
  6. <jsr107:defaults>
  7. <!-- 定义别名为 "studentCache"的缓存,继承自缓存模板“heap - cache” -->
  8. <jsr107:cache name="studentCache" template="heap-cache"/>
  9. </jsr107:defaults>
  10. </service>
  11. <cache-template name="heap-cache">
  12. <!-- 添加缓存事件监听器,当以下事件发生时,被EventLogger记录 -->
  13. <listeners>
  14. <listener>
  15. <class>com.jimzhang.listener.EventLoggerListener</class>
  16. <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
  17. <event-ordering-mode>UNORDERED</event-ordering-mode>
  18. <events-to-fire-on>CREATED</events-to-fire-on>
  19. <events-to-fire-on>UPDATED</events-to-fire-on>
  20. <events-to-fire-on>EXPIRED</events-to-fire-on>
  21. <events-to-fire-on>REMOVED</events-to-fire-on>
  22. <events-to-fire-on>EVICTED</events-to-fire-on>
  23. </listener>
  24. </listeners>
  25. <resources>
  26. <!-- 堆设置保存2000个条目 -->
  27. <heap unit="entries">2000</heap>
  28. <!-- 存储空间100M -->
  29. <offheap unit="MB">100</offheap>
  30. </resources>
  31. </cache-template>
  32. </config>

5. 实现JCacheManagerCustomizer方法,创建缓存

  1. @Component
  2. // 开启缓存
  3. @EnableCaching
  4. public class JSRCacheConfiguration implements JCacheManagerCustomizer {
  5. @Override
  6. public void customize(CacheManager cacheManager) {
  7. cacheManager.createCache("studentCache", new MutableConfiguration<>()
  8. .setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, 10)))
  9. .setStoreByValue(false)
  10. .setStatisticsEnabled(true));
  11. }
  12. }

6. 日志监听器(非必须)

EventLoggerListener

  1. public class EventLoggerListener implements CacheEventListener<Object, Object> {
  2. private static final Logger LOGGER = LoggerFactory.getLogger(EventLoggerListener.class);
  3. @Override
  4. public void onEvent(CacheEvent<?, ?> event) {
  5. LOGGER.info("Event: " + event.getType() + " Key: " + event.getKey() + " old value: " + event.getOldValue()
  6. + " new value: " + event.getNewValue());
  7. }
  8. }

7. 最后测试即可。

单元测试:

  1. @Test
  2. public void test() {
  3. Student student = studentService.findById(1L);
  4. logger.info("学生信息----:" + student.toString());
  5. Student student2 = studentService.findById(1L);
  6. logger.info("学生信息2----:" + student2.toString());
  7. }

ehcache 2.x

1. 引入依赖

  1. <!-- Spring 对Cache 的支持 -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-cache</artifactId>
  5. </dependency>
  6. <!--EHCache 相关依赖 2.x-->
  7. <dependency>
  8. <groupId>net.sf.ehcache</groupId>
  9. <artifactId>ehcache</artifactId>
  10. </dependency>

2. 创建缓存配置文件

2.1 xml方式

创建ehcache.xml文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
  4. updateCheck="false">
  5. <defaultCache
  6. eternal="false"
  7. maxElementsInMemory="1000"
  8. overflowToDisk="false"
  9. diskPersistent="false"
  10. timeToIdleSeconds="0"
  11. timeToLiveSeconds="600"
  12. memoryStoreEvictionPolicy="LRU" />
  13. <cache
  14. name="studentCache"
  15. eternal="false"
  16. maxElementsInMemory="100"
  17. overflowToDisk="false"
  18. diskPersistent="false"
  19. timeToIdleSeconds="0"
  20. timeToLiveSeconds="300"
  21. memoryStoreEvictionPolicy="LRU" />

配置文件类

  1. @SpringBootConfiguration
  2. /** 启用缓存 **/
  3. @EnableCaching
  4. public class EHCacheConfiguration {
  5. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  6. /**
  7. * ehcache 主要的管理器
  8. *
  9. * @param bean
  10. * @return
  11. */
  12. @Bean
  13. public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean) {
  14. logger.info("CacheConfiguration.ehCacheCacheManager()");
  15. // return new EhCacheCacheManager(bean.getObject());
  16. return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
  17. }
  18. /**
  19. * 默认情况下,其实可以不配置,EhCacheCacheConfiguration 类中默认使用EHCache 就是使用的classpath:ehcache.xml
  20. *
  21. * @return
  22. */
  23. @Bean
  24. public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
  25. logger.info("CacheConfiguration.ehCacheManagerFactoryBean()");
  26. EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
  27. cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("config/ehcache.xml"));
  28. //也说是说通过这个来设置cache的基地是这里的Spring独用,还是跟别的(如hibernate的Ehcache共享)
  29. cacheManagerFactoryBean.setShared(true);
  30. return cacheManagerFactoryBean;
  31. }
  32. }

在application.properties配置文件制定ehcache.xml的位置

  1. spring.cache.ehcache.config=classpath:config/another-config.xml

其实,可以不指定,Springboot只要发现了该配置文件,就会创建EhCache的缓存管理器。

2.2 注解方式

代替ehcache.xml配置文件

  1. @SpringBootConfiguration
  2. @EnableCaching
  3. public class CacheConfig {
  4. @Bean
  5. CacheManager cacheManager() {
  6. return new EhCacheCacheManager(ehCacheManager());
  7. }
  8. @Bean(destroyMethod = "shutdown")
  9. net.sf.ehcache.CacheManager ehCacheManager() {
  10. CacheConfiguration cacheConfiguration = new CacheConfiguration();
  11. cacheConfiguration.setName("studentCache");
  12. cacheConfiguration.setTimeToLiveSeconds(600); // 保存10分钟
  13. cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
  14. cacheConfiguration.setMaxEntriesLocalHeap(1000);
  15. net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
  16. config.addCache(cacheConfiguration);
  17. return new net.sf.ehcache.CacheManager(config);
  18. }
  19. }

3. 方法中使用缓存注解

  1. @Cacheable(value = "studentCache", key = "'student_'+#id")
  2. @Override
  3. public Student findById(Long id) {
  4. return studentDao.findOne(id);
  5. }

4. 单元测试

  1. @Test
  2. public void test() {
  3. Student student = studentService.findById(1L);
  4. logger.info("学生信息----:" + student.toString());
  5. Student student2 = studentService.findById(1L);
  6. logger.info("学生信息2----:" + student2.toString());
  7. }

参考:

Guava Cache

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注