SpringBoot整合redis

1、redis下载和本地启动

下载地址:https://github.com/tporadowski/redis/releases

下载 Redis-x64-xxx.zip压缩包,将其解压到名为 redis的文件夹。

本地启动:进入redis目录,在cmd窗口运行命令:redis-server.exe redis.windows.conf ,redis便可成功启动。

下图是redis启动成功的界面:
在这里插入图片描述

2、pom文件中导入redis依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

3、yml文件中配置redis的链接

spring:
# 配置redis连接
redis:
 # Redis服务器地址
 host: 127.0.0.1
 # Redis服务器连接端口
 port: 6379
 jedis:
   pool:
     # 连接池最大连接数(使用负值表示没有限制)
     max-active: 10
     # 连接池最大阻塞等待时间(使用负值表示没有限制)
     max-wait: 1000
 # 连接超时时间(毫秒)
 connect-timeout: 1000

4、配置RedisTemplate<String, Object>

SpringBoot框架默认在org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration类下配置了两个RedisTemplate:

  • RedisTemplate<Object, Object>

    key和value都是Object类型,并且都需要实现Serializable接口。

  • StringRedisTemplate,即RedisTemplate<String, String>

    key和value都是String类型,当需要存储实体类时,需要先将其转为JSON格式的字符串,再存入Redis。

RedisAutoConfiguration类源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"})
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

SpringBoot配置好的两个RedisTemplate都不太好用,因为将实体类手动转换为JSON格式字符串的过程比较麻烦,所以在项目中可以自己配置一个RedisTemplate<String, Object>的Bean。代码如下:

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        // key使用StringRedisSerializer进行序列化
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        // value使用GenericJackson2JsonRedisSerializer进行序列化
        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        return template;
    }
}

那么RedisTemplate配置连接的工厂RedisConnectionFactory是从哪儿来的呢?我们不妨在redisTemplate方法中先将RedisConnectionFactory对象打印出来,看看它到底是什么东西。

System.out.println("factory: " + factory);

由于配置类是在SpringBoot项目启动时就加载到Spring容器中,所以在项目启动时,控制台展示了上面语句对factory的打印。

factory: org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@1f3165e7

可以看出,这个RedisConnectionFactory工厂对象实际是LettuceConnectionFactory的一个实例,LettuceConnectionFactory实现了RedisConnectionFactory接口。是那么LettuceConnectionFactory的实例又是在哪加载的呢?

继续探索发现,在org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration类中创建了LettuceClientConfiguration实例并放到了Spring容器中。部分代码如下:

@Bean
@ConditionalOnMissingBean({RedisConnectionFactory.class})
LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) {
    LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
    return this.createLettuceConnectionFactory(clientConfig);
}

5、redis测试应用

应用场景:如果redis中存在key值对应的缓存数据,则取redis中的数据;如果redis中不存在key值对应的缓存数据,则去从数据库中取值。

service层代码如下:

/**
 * 这里仅展示了Service层代码
 * */
@Service
@Transactional
public class TestServiceImpl implements ITestService {

    @Resource
    private TestMapper testMapper;

    @Resource
    private RedisTemplate<String, Object> template;

    @Override
    public UserVO test(int userId) {

        ValueOperations<String, Object> redisString = template.opsForValue();
        String redisKey = "user";
        UserVO userVO = null;
        Instant start = Instant.now();
        if (redisString.get(redisKey) == null) {
            System.out.println("====从数据库中获取数据====");
            userVO = testMapper.test(userId);
            redisString.set(redisKey, userVO, 7L, TimeUnit.DAYS);
        } else {
            System.out.println("====从redis中获取数据====");
            userVO = (UserVO) redisString.get(redisKey);
        }
        System.out.println(userVO);
        Instant end = Instant.now();
        System.out.println("耗时:" + Duration.between(start, end).toMillis() + "毫秒");
        return userVO;
    }
}

redis设置key值时有两点注意事项:

  • 设置key时必须设置过期时间。
  • 设置key时必须加上指定前缀,方便批量操作。

启动SpringBoot项目后,用Postman去调本地接口,程序运行控制台打印结果如下:

====从数据库中获取数据====
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@391c1891]
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@45a49218] will be managed by Spring
==>  Preparing: select id as userId, name as userName from test_users where id = ?; 
==> Parameters: 1011(Integer)
<==    Columns: userId, userName
<==        Row: 1011, 橘子右
<==      Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@391c1891]
UserVO(userId=1011, userName=橘子右)
耗时:1657毫秒
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@391c1891]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@391c1891]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@391c1891]

Postman第一次调本地接口时已经将获取到的数据放到了redis缓存中。由于SpringBoot项目懒加载机制的特性,启动后第一次操作往往会花费较多时间,为了保证公平性,重启SpringBoot项目后,再次用用Postman去调本地接口,程序运行控制台打印结果如下:

====从redis中获取数据====
UserVO(userId=1011, userName=橘子右)
耗时:1589毫秒

经过多次试验,由结果可知,从redis中取数据要比从数据库中取数据更快。由于查询的数据量较小,所以使用redis只比查数据库快那么一丢丢,但是如果操作的数据量较大的话,那么它们之间的性能差距就会更加明显。

为什么使用redis缓存的操作会更快?原因是Oracle、Mysql这些关系型数据库的数据存放在磁盘里,在获取数据时需要执行I/O操作;而redis是一个内存数据库,它的数据都存放在内存中。因为内存的读写速度相较于磁盘的读写要快很多,因此,从redis中获取数据更快,所以redis常被用来做页面、数据的缓存。

在SpringBoot中配置了Redis后,启动项目会有如下日志信息:

INFO 1696 --- [main] .s.d.r.c.RepositoryConfigurationDelegate: Multiple Spring Data modules found, entering strict repository configuration mode!

虽然这条日志信息是info级别的,但是该日志信息后面由感叹号(一般info级别是没有感叹号的),所以值得深究一下。

由控制台报错信息可知,该报错信息是在org.springframework.data.repository.config包下的RepositoryConfigurationDelegate中的multipleStoresDetected()方法里打印到日志的。multipleStoresDetected()方法如下:

private boolean multipleStoresDetected() {
 boolean multipleModulesFound = SpringFactoriesLoader.loadFactoryNames(RepositoryFactorySupport.class,
			this.resourceLoader.getClassLoader()).size() > 1;
 if (multipleModulesFound) {
     logger.info("Multiple Spring Data modules found, entering strict repository configuration mode!");
 }
 return multipleModulesFound;
}

从代码可知,如果RepositoryFactorySupport这个抽象类及其字类如果超过一个,就会显示该日志信息。

全局搜RepositoryFactorySupport,可以发现有两处RepositoryFactorySupport的应用:

  • 第一处是spring-data-keyvalue.jar包的META-INF目录下的spring.factories文件。代码如下:

    org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory
    
  • 第二处是spring-data-redis.jar包的META-INF目录下的spring.factories文件。代码如下:

    org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.redis.repository.support.RedisRepositoryFactory
    

由此可知,正是由于存在两个RepositoryFactorySupport,所以会有“Multiple Spring Data modules found”的日志信息。

解决方案:使用如下两种方案中的任何一种,重启SpringBoot项目,“Multiple Spring Data modules found”的日志信息将会消失。

  • 方案一:yml文件中关闭redis的repository功能。

    spring:
      data:
        redis:
          repositories:
            enabled: false
    
  • 方案二:在SpringBoot项目的启动类中排除repository自动装载。

    @SpringBootApplication(exclude = RedisRepositoriesAutoConfiguration.class)
    @MapperScan(basePackages = "com.example.demo.dao")
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
注:下文中的 *** 代表文件名中的组件名称。 # 包含: 中文-英文对照文档:【***-javadoc-API文档-中文(简体)-英语-对照版.zip】 jar包下载地址:【***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: 中文-英文对照文档,中英对照文档,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【***.jar中文文档.zip】,再解压其中的 【***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·本文档为双语同时展示,一行原文、一行译文,可逐行对照,避免了原文/译文来回切换的麻烦; ·有原文可参照,不再担心翻译偏差误导; ·边学技术、边学英语。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值