一个系统在于数据库交互的过程当中,内存的速度远远快于硬盘速度,当咱们重复地获取相同数据时,咱们一次又一次地请求数据库或远程服务,者无疑时性能上地浪费(这会致使大量时间被浪费在数据库查询或者远程方法调用上导致程序性能恶化),因而有了“缓存”。 本文将介绍在spring boot项目开发中怎样使用spring提供的Spring Cache 与最近很火的 Redis 数据库来实现数据的缓存。html
Spring Cache
是Spring
框架提供的对缓存使用的抽象类,支持多种缓存,好比Redis
、EHCache
等,集成很方便。同时提供了多种注解来简化缓存的使用,可对方法进行缓存。java
Spring Cache提供了一些供咱们使用的SpEL上下文数据,下表直接摘自Spring官方文档:git
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | #root.methodName |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象 | #root.target |
targetClass | root对象 | 当前被调用的目标对象类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache | #root.caches[0].name |
argument name | 执行上下文 | 当前被调用的方法的参数,如findById(Long id),咱们能够经过#id拿到参数 | #user.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行以后的判断有效,如‘unless’,'cache evict'的beforeInvocation=false) | #result |
其余关于 Cache 详细配置或注解,请参考文章基于Redis的Spring cache 缓存介绍或spring官方文档github
Redis 是彻底开源免费的,遵照BSD协议,是一个高性能的key-value数据库。web
Redis 与其余 key - value 缓存产品有如下三个特色:redis
Redis 的安装和使用请自行Google 。spring
咱们要把一个查询函数加入缓存功能,大体须要三步。数据库
本实例没有存入MySQL数据库,主要是为了方便实践,实际使用中你们能够把service层中的方法改成数据库操做代码便可。json
<!-- springboot redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
复制代码
注: 其实咱们从官方文档能够看到spring-boot-starter-data-redis 已经包含了jedis客户端,咱们在使用jedis链接池的时候没必要再添加jedis依赖。缓存
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @Author: MaoLin * @Date: 2019/3/26 17:04 * @Version 1.0 */ @Configuration @EnableCaching public class RedisConfig { /** * 申明缓存管理器,会建立一个切面(aspect)并触发Spring缓存注解的切点(pointcut) * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值 * @return */ @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return RedisCacheManager.create(redisConnectionFactory); } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { // 建立一个模板类 RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); // 将刚才的redis链接工厂设置到模板类中 template.setConnectionFactory(factory); // 设置key的序列化器 template.setKeySerializer(new StringRedisSerializer()); // 设置value的序列化器 //使用Jackson 2,将对象序列化为JSON Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //json转对象类,不设置默认的会将json转成hashmap ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } } 复制代码
server: port: 8080 spring: # redis相关配置 redis: database: 0 host: localhost port: 6379 password: 123456 jedis: pool: # 链接池最大链接数(使用负值表示没有限制) max-active: 8 # 链接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # 链接池中的最大空闲链接 max-idle: 8 # 链接池中的最小空闲链接 min-idle: 0 # 链接超时时间(毫秒)默认是2000ms timeout: 2000ms cache: redis: ## Entry expiration in milliseconds. By default the entries never expire. time-to-live: 1d #写入redis时是否使用键前缀。 use-key-prefix: true 复制代码
import lombok.Data; import java.io.Serializable; /** * @Author: MaoLin * @Date: 2019/3/24 14:36 * @Version 1.0 */ @Data //lombok依赖,可省略get set方法 public class User implements Serializable { private int userId; private String userName; private String userPassword; public User(int userId, String userName, String userPassword) { this.userId = userId; this.userName = userName; this.userPassword = userPassword; } } 复制代码
import com.ml.demo.entity.User; import org.springframework.stereotype.Service; /** * @Author: MaoLin * @Date: 2019/3/24 14:38 * @Version 1.0 */ @Service public class UserDao { public User getUser(int userId) { System.out.println("执行此方法,说明没有缓存,若是没有走到这里,就说明缓存成功了"); User user = new User(userId, "没有缓存_"+userId, "password_"+userId); return user; } public User getUser2(int userId) { System.out.println("执行此方法,说明没有缓存,若是没有走到这里,就说明缓存成功了"); User user = new User(userId, "name_nocache"+userId, "nocache"); return user; } } 复制代码
开箱即用
的Springimport com.ml.demo.dao.UserDao; import com.ml.demo.entity.User; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @Author: MaoLin * @Date: 2019/3/26 17:03 * @Version 1.0 */ @RestController public class testController { @Resource private UserDao userDao; /** * 查询出一条数据而且添加到缓存 * * @param userId * @return */ @RequestMapping("/getUser") @Cacheable("userCache") public User getPrud(@RequestParam(required = true) String userId) { System.out.println("若是没有缓存,就会调用下面方法,若是有缓存,则直接输出,不会输出此段话"); return userDao.getUser(Integer.parseInt(userId)); } /** * 删除一个缓存 * * @param userId * @return */ @RequestMapping(value = "/deleteUser") @CacheEvict("userCache") public String deleteUser(@RequestParam(required = true) String userId) { return "删除成功"; } /** * 添加一条保存的数据到缓存,缓存的key是当前user的id * * @param user * @return */ @RequestMapping("/saveUser") @CachePut(value = "userCache", key = "#result.userId +''") public User saveUser(User user) { return user; } /** * 返回结果userPassword中含有nocache字符串就不缓存 * * @param userId * @return */ @RequestMapping("/getUser2") @CachePut(value = "userCache", unless = "#result.userPassword.contains('nocache')") public User getUser2(@RequestParam(required = true) String userId) { System.out.println("若是走到这里说明,说明缓存没有生效!"); User user = new User(Integer.parseInt(userId), "name_nocache" + userId, "nocache"); return user; } @RequestMapping("/getUser3") @Cacheable(value = "userCache", key = "#root.targetClass.getName() + #root.methodName + #userId") public User getUser3(@RequestParam(required = true) String userId) { System.out.println("若是第二次没有走到这里说明缓存被添加了"); return userDao.getUser(Integer.parseInt(userId)); } } 复制代码
接下来最重要的工做:跑起来
存入数据:
从缓存读取数据:
删除缓存:
再读取:
此时没有缓存,调用方法,并存入缓存
此为cache中的条件:含有nocache字符时不存入缓存。本身去探索就好。
为了实现缓存,在网上参考了不少博客、资料,可是都不尽人意,后来通过几天的学习,发现Spring提供了缓存对象,我结合redis,优雅地实现了缓存。学习代码是个艰辛的过程,我在学习这部分时看了好多书,找了好多博客资料,终于找到了合适的缓存方案,很开心,不过这还只是一小步啊,加油!!!