Redis 是目前业界使用最普遍的内存数据存储。相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化。除此以外,Redis 还提供一些类数据库的特性,好比事务,HA,主从库。能够说 Redis 兼具了缓存系统和数据库的一些特性,所以有着丰富的应用场景。本文介绍 Redis 在 Spring Boot 中两个典型的应用场景。java
若是在 Java 应用中使用过 Redis 缓存,那么对 Jedis
必定不陌生, Lettuce
和 Jedis
同样,都是链接 Redis Server
的客户端程序。Jedis
在实现上是直连 Redis Server
,多线程环境下非线程安全,除非使用链接池,为每一个 Jedis
实例增长物理链接。 Lettuce
基于 Netty
的链接实例(StatefulRedisConnection),能够在多个线程间并发访问,且线程安全,知足多线程环境下的并发访问,同时它是可伸缩的设计,一个链接实例不够的状况也能够按需增长链接实例。git
RedisTemplate
来使用Spring Cache
集成 Redis
Spring Session
作 Session
共享代码清单:spring-boot-redis/pom.xmlgithub
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Spring Boot 2.x
后底层再也不是使用 Jedis
,而是换成了 Lettuce
,如图:redis
链接池,如不引入启动会报错。Spring Session
引入,用做共享 Session
。代码清单:spring-boot-redis/src/main/resources/application.ymlweb
server: port: 8080 servlet: session: timeout: 30m spring: application: name: spring-boot-redis cache: # 使用了Spring Cache后,能指定spring.cache.type就手动指定一下,虽然它会自动去适配已有Cache的依赖,但前后顺序会对Redis使用有影响(JCache -> EhCache -> Redis -> Guava) type: REDIS redis: host: 192.168.0.128 port: 6379 password: 123456 # 链接超时时间(ms) timeout: 10000 # Redis默认状况下有16个分片,这里配置具体使用的分片,默认是0 database: 0 lettuce: pool: # 链接池最大链接数(使用负值表示没有限制) 默认 8 max-active: 100 # 链接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 max-wait: -1 # 链接池中的最大空闲链接 默认 8 max-idle: 8 # 链接池中的最小空闲链接 默认 0 min-idle: 0
这里的配置很少解释,须要解释的已经标注注释。redis
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/model/User.javaspring
@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private static final long serialVersionUID = 662692455422902539L; private Long id; private String name; private int age; }
默认状况下的模板只能支持 RedisTemplate<String, String>
,也就是只能存入字符串,这在开发中是不友好的,因此自定义模板是颇有必要的,当自定义了模板又想使用 String
存储这时候就可使用 StringRedisTemplate
的方式,它们并不冲突,添加配置类 RedisCacheConfig.java
,代码以下:sql
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/config/RedisCacheConfig.java数据库
@Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisCacheConfig { @Bean public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { RedisTemplate<String, Serializable> template = new RedisTemplate<>(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
代码清单:apache
@RestController @Slf4j public class UserController { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired RedisTemplate<String, Serializable> redisCacheTemplate; @Autowired UserService userService; @GetMapping("/test") public void test() { stringRedisTemplate.opsForValue().set("geekdigging", "https://www.geekdigging.com/"); log.info("当前获取对象:{}",stringRedisTemplate.opsForValue().get("geekdigging")); redisCacheTemplate.opsForValue().set("geekdigging.com", new User(1L, "geekdigging", 18)); User user = (User) redisCacheTemplate.opsForValue().get("geekdigging.com"); log.info("当前获取对象:{}", user); } }
启动服务,打开浏览器访问连接:http://localhost:8080/test ,查看控制台日志打印,以下:浏览器
2019-09-24 23:49:30.191 INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前获取对象:https://www.geekdigging.com/ 2019-09-24 23:49:30.243 INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前获取对象:User(id=1, name=geekdigging, age=18)
测试成功。
Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 Redis),而是一个对缓存使用的抽象,经过在既有代码中添加少许它定义的各类 annotation,即可以达到缓存方法的返回对象的效果。
Spring Cache 具有至关的好的灵活性,不只可以使用 SpEL(Spring Expression Language)来定义缓存的 key 和各类 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache、Redis、Guava 的集成。
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/service/UserService.java
public interface UserService { User save(User user); User get(Long id); void delete(Long id); }
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/service/impl/UserServiceImpl.java
@Service @Slf4j public class UserServiceImpl implements UserService { private static final Map<Long, User> USER_MAP = new HashMap<>(); static { USER_MAP.put(1L, new User(1L, "geekdigging.com", 18)); USER_MAP.put(2L, new User(2L, "geekdigging.com", 19)); USER_MAP.put(3L, new User(3L, "geekdigging.com", 20)); } @CachePut(value = "user", key = "#user.id") @Override public User save(User user) { USER_MAP.put(user.getId(), user); log.info("进入 save 方法,当前存储对象:{}", user); return user; } @Cacheable(value = "user", key = "#id") @Override public User get(Long id) { log.info("进入 get 方法,当前获取对象:{}", USER_MAP.get(id)); return USER_MAP.get(id); } @CacheEvict(value = "user", key = "#id") @Override public void delete(Long id) { USER_MAP.remove(id); log.info("进入 delete 方法,删除成功"); } }
为了方便演示数据库操做,直接定义了一个 Map<Long, User> USER_MAP
,这里的核心就是三个注解 @Cacheable
、 @CachePut
、 @CacheEvict
。
根据方法的请求参数对其结果进行缓存
@Cacheable(value="user",key="#userName")
)@Cacheable(value="user")
或者 @Cacheable(value={"user1","use2"})
)@Cacheable(value = "user", key = "#id",condition = "#id < 10")
)根据方法的请求参数对其结果进行缓存,和 @Cacheable 不一样的是,它每次都会触发真实方法的调用
根据条件对缓存进行清空
@CacheEvict(value = "user", key = "#id", allEntries = true)
)@CacheEvict(value = "user", key = "#id", beforeInvocation = true)
)代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/SpringBootRedisApplication.java
@SpringBootApplication @EnableCaching public class SpringBootRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootRedisApplication.class, args); } }
@EnableCaching
开启 Spring Session。代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/controller/UserController.java
@GetMapping("/test1") public void test1() { User user = userService.save(new User(4L, "geekdigging.com", 35)); log.info("当前 save 对象:{}", user); user = userService.get(1L); log.info("当前 get 对象:{}", user); userService.delete(5L); }
启动服务,打开浏览器访问连接:http://localhost:8080/test ,刷新页面,控制台日志打印以下:
2019-09-25 00:07:21.887 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 进入 save 方法,当前存储对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:07:21.897 INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前 save 对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:07:21.899 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 进入 get 方法,当前获取对象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:07:21.900 INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前 get 对象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:07:21.901 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 进入 delete 方法,删除成功
再次刷新页面,查看控制台日志:
2019-09-25 00:08:54.076 INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl : 进入 save 方法,当前存储对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:08:54.077 INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController : 当前 save 对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:08:54.079 INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController : 当前 get 对象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:08:54.079 INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl : 进入 delete 方法,删除成功
结果和咱们指望的一致,能够看到增删改查中,查询是没有日志输出的,由于它直接从缓存中获取的数据,而添加、修改、删除都是会进入 UserServiceImpl
的方法内执行具体的业务代码。
Spring Session 提供了一套建立和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据,以此来解决 Session 共享的问题。
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/SpringBootRedisApplication.java
@SpringBootApplication @EnableCaching @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) public class SpringBootRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootRedisApplication.class, args); } }
application.yml
中的 server.session.timeout
属性再也不生效。代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/controller/UserController.java
@GetMapping("/getBlogUrl") public String getSessionId(HttpServletRequest request) { String url = (String) request.getSession().getAttribute("url"); if (StringUtils.isEmpty(url)) { request.getSession().setAttribute("url", "https://www.geekdigging.com/"); } log.info("获取session内容为: {}", request.getSession().getAttribute("url")); return request.getRequestedSessionId(); }
启动服务,打开浏览器访问连接:http://localhost:8080/getBlogUrl ,查看 Redis 当前存储内容,以下图:
其中 1569339180000 为失效时间,意思是这个时间后 Session 失效, b2522824-1094-478e-a435-554a551bc8bb 为 SessionId 。
按照上面的步骤在另外一个项目中再次配置一次,启动后自动就进行了 Session 共享。
http://emacoo.cn/backend/spri...
https://blog.battcn.com/2018/...
若是个人文章对您有帮助,请扫码关注下做者的公众号:获取最新干货推送:)