整合Redis
# 550.整合Redis
接下来我们试试整合NoSQL,例如Redis
由于本系列教程是SpringBoot,如果不熟悉Redis的话,可以参考下Redis教程 (opens new window)
# 引入依赖
SpringBoot已经提供了相关的starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2
3
4
# 自动配置分析
可以分析下其依赖:
引入了keyvalue等操作数据的,以及lettuce这个客户端用来操作Redis(基于Netty)
在SpringBoot的自动配置类中,有一个叫做RedisAutoConfiguration
,就是配置了和Redis相关的:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
配置属性的类RedisProperties
,就是会将spring.redis
开头的属性绑定:
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:password@example.com:6379
*/
private String url;
//..................
2
3
4
5
6
7
8
9
10
11
12
13
14
15
还引入了LettuceConnectionConfiguration
,这个就是客户端连接的配置;该配置会放入一个客户端资源DefaultClientResources
,以及Lettuce的连接工厂
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(ClientResources.class)
DefaultClientResources lettuceClientResources() {
return DefaultClientResources.create();
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
LettuceConnectionFactory redisConnectionFactory() {
//.....
}
2
3
4
5
6
7
8
9
10
11
除此之外,还引入了JedisConnectionConfiguration
,这也是一个客户端工具,至于到底是用Jedis
还是Lettuce
,就看引入了哪个依赖了,或者指定配置。
在自动配置类中,还注入了RedisTemplate
,StringRedisTemplate
。Redis是通过key-value方式存储数据的,第一个template
中,可以用Object来表示key和value;而第二个template
,则是用于key和value都是String的情况(因为String的情况比较多)。
# 准备Redis
读者可以使用本地的Redis,或者使用云服务器厂商的Redis(按量访问)。
然后新增配置:
spring:
redis:
url: redis://localhost:6379/0
2
3
在测试类中中新增方法:
class LearnSpringBootWebAdminApplicationTests {
@Autowired
StringRedisTemplate redisTemplate;
@Test
void testRedis(){
ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
stringStringValueOperations.set("hello", "world");
String hello = stringStringValueOperations.get("hello");
log.info("hello:{}", hello);
}
//............
2
3
4
5
6
7
8
9
10
11
12
13
14
15
然后可以运行该测试方法,并且可以用其他Redis客户端来查看数据
# 切换至Jedis
默认使用的是lettuce,如何使用Jedis呢?
- 引入Jedis的依赖(版本号已经默认配置了)
- 去除lettuce的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
然后我们可以打印下工厂的类型:
class LearnSpringBootWebAdminApplicationTests {
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Test
void testRedis(){
log.info("redis类型:{}", redisConnectionFactory.getClass());
}
//............
2
3
4
5
6
7
8
9
10
11
12
运行结果:
redis类型:class org.springframework.data.redis.connection.jedis.JedisConnectionFactory
除此之外,还可以对连接池信息进行配置,例如:
spring:
redis:
url: redis://localhost:6379/0
jedis:
pool:
max-active: 8
max-wait: -1ms
2
3
4
5
6
7
# 统计功能
我们可以做个统计,例如访问了多少次/sql请求,然后展示到页面上:
我们新增一个拦截器:
package com.peterjxl.learnspringbootwebadmin.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component // 将拦截器交给Spring管理
public class RedisUrlCountInterceptor implements HandlerInterceptor {
@Autowired
StringRedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI(); // 获取请求的url
// 访问量加1
redisTemplate.opsForValue().increment(uri);
return true; // 放行
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
然后讲拦截器加入到SpringBoot中:
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Autowired
RedisUrlCountInterceptor redisUrlCountInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有请求,包括静态资源
.excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**", "/sql");
registry.addInterceptor(redisUrlCountInterceptor)
.addPathPatterns("/**") // 拦截所有请求,包括静态资源
.excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意,不能用registry.addInterceptor(new RedisUrlCountInterceptor)
,因为用new的话,我们拦截器中的StringRedisTemplate
就不会注入了
接下来我们可以重启下项目,然后多访问一些页面,可以看到Redis中确实有这些数据:
接下来我们展示在首页中,首先在首页的controller中放入次数:
@GetMapping("/main.html")
public String mainPage(HttpSession session, Model model) {
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
String s = opsForValue.get("/main.html");
String s1 = opsForValue.get("/sql");
model.addAttribute("mainCount", s);
model.addAttribute("sqlCount", s1);
return "main";
}
2
3
4
5
6
7
8
9
10
11
然后在首页中修改:
效果:
# 扩展
Filter 和 Interceptor 几乎都有同样的功能,用哪个好?
- Filter 是Servlet 原生的API ,没有Spring也能使用
- Interceptor 是Spring定义的接口,只能在Spring中使用,好处是可以使用Spring的功能(例如注入)
# 源码
已将本文源码上传到Gitee (opens new window)或GitHub (opens new window) 的分支demo16,读者可以通过切换分支来查看本文的示例代码