Redis+SpringBoot+SpringCache基础项目搭建

1. 环境准备

系统工具 版本 备注
Windows 10
Idea 2019.3
JDK 1.8
Redis 5.0.3 参考 docker+docker compose 部署Redis

2.创建工程

由于使用的是Idea,因此我们可以借用Idea提供的工程创建工具快速创建一个SpringBoot+Redis+SpringCache工程,具体步骤如下:

选择新建工程,选择项目类型如下:
在这里插入图片描述

完成Maven工程基本定义:
在这里插入图片描述
勾选需要的依赖:

这里我勾选了DeveloperTools-Lombok、Web-Spring Web、SQL-Mybatis、MySQL driver、Spring Data JDBC、NoSQL-Spring Data Redis

由于工程需要添加commons-pool、druid、swagger相关依赖

最终生成工程POM如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency><!--添加Swagger依赖 -->
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency><!--添加Swagger-UI依赖 -->
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
<repositories>

    <repository>
        <id>public</id>
        <name>aliyun nexus</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
    </repository>


</repositories>
<pluginRepositories>
<pluginRepository>
    <id>public</id>
    <name>aliyun nexus</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <releases>
        <enabled>true</enabled>
    </releases>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
</pluginRepository>
</pluginRepositories>
</project>

初始项目创建完毕。
项目结构如下:
在这里插入图片描述

3.编写项目配置

3.1 整体项目配置

SpringBoot项目是使用application.yml/application.properties完成项目配置,主要配置分为项目端口配置、Mysql相关配置、MyBatis相关配置、Redis相关配置、SpringCache相关配置、Swagger相关配置。
根据本地环境编写application.yml如下:

server:
  port: 8080


spring:
  datasource:
    name: mysql_test
    type: com.alibaba.druid.pool.DruidDataSource
    #druid相关配置
    druid:
      #监控统计拦截的filters
      filters: stat
      driver-class-name: com.mysql.jdbc.Driver
      #基本属性
      url: jdbc:mysql://localhost:3306/redis_demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
      username: root
      password: 123456
      #配置连接池 初始化大小/最小/最大
      initial-size: 1
      min-idle: 1
      max-active: 20
      #获取连接等待超时时间
      max-wait: 60000
      #间隔多久进行一次检测,检测需要关闭的空闲连接
      time-between-eviction-runs-millis: 60000
      #一个连接在池中最小生存的时间
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      #打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
      pool-prepared-statements: false
      max-pool-prepared-statement-per-connection-size: 20
  #redis相关配置 host把端口加进去(详见RedisConfig的LettuceConnectionFactory)
  redis:
    database: 0
    host: 192.168.1.130
    port: 6379
    timeout: 5000
    lettuce:
      pool:
        max-idle: 8
        max-wait: -1
        max-active: 8
        min-idle: 0
  ##Spring cache相关配置
  cache:
    cache-names: ca1
    # 过期时间1天
    expireTime: 86400
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.demo



#swagger
swagger:
  title: spring-boot-starter-swagger
  description: Starter for swagger 2.x
  version: 1.1.0.RELEASE
  license: Apache License, Version 2.0
  license-url: https://www.apache.org/licenses/LICENSE-2.0.html
  terms-of-service-url: https://github.com/github-sun/Funs
  base-package: com.example.redis.demo
  contact:
    name: xxx
    email: [email protected]

3.1 Redis配置

Redis的配置这里主要是RedisTemplate相关,RedisTemplate是Spring Data Redis提供给用户的最高级的抽象客户端,用户可直接通过RedisTemplate进行多种操作。但是默认的RedisTemplate是RedisTemplate<K, V>,K、V都是泛型,假如直接用 RedisTemplate,并没有指定序列化器,会导致存什么类型的Value都会带BOM_UTF8,标识编码方式的字符,像下图这个样子:
在这里插入图片描述

因此我们需要为不同类型的V制定不同类型的序列化器,好让他能解析成我们能看的样子。

编写RedisConfig如下:


/**
 * Redis配置类
 */
@Configuration
// 控制配置类的加载顺序,先加载 RedisAutoConfiguration.class 再加载该类,这样才能覆盖默认的 RedisTemplate
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
    /**
     * 自定义 redisTemplate (方法名一定要叫 redisTemplate 因为 @Bean 是根据方法名配置这个bean的name的)
     * 默认的 RedisTemplate<K,V> 为泛型,使用时不太方便,自定义为 <String, Object>
     * 默认序列化方式为 JdkSerializationRedisSerializer 序列化后的内容不方便阅读,改为序列化成 json
     *
     * @param redisConnectionFactory
     * @return
     */
     @Bean
    RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 创建并配置自定义 RedisTemplateRedisOperator
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 将 key 序列化成字符串
        template.setKeySerializer(new StringRedisSerializer());
        // 将 hash 的 key 序列化成字符串
        template.setHashKeySerializer(new StringRedisSerializer());
        // 将 value 序列化成 json
//        template.setValueSerializer(jacksonSerializer);
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        // 将 hash 的 value 序列化成 json
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        template.afterPropertiesSet();
        return template;
    }
}

这样结果就会像下图这个样子:
在这里插入图片描述

3.2 SpringCache配置

SpringCache是什么呢?SpringCache之于Redis就相当于JPA之于Mybatis。

Spring Cache 提供了 @Cacheable 、@CachePut 、@CacheEvict 、@Caching 等注解,在方法上使用。通过注解 Cache 可以实现类似事务一样、缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码。
核心思想:当我们调用一个方法时会把该方法的参数和返回结果最为一个键值对存放在缓存中,等下次利用同样的参数来调用该方法时将不会再执行,而是直接从缓存中获取结果进行返回。

3.2.1 SpringCache配置

1.@EnableCaching

开启缓存功能,一般放在启动类上。

2.@CacheConfig

当我们需要缓存的地方越来越多,你可以使用@CacheConfig(cacheNames = {“cacheName”})注解在 class 之上来统一指定value的值,这时可省略value,如果你在你的方法依旧写上了value,那么依然以方法的value值为准。

3.@Cacheable

根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key
keyGenerator key的生成器。key/keyGenerator二选一使用
cacheManager 指定缓存管理器
cacheResolver 指定获取解析器
condition 条件符合则缓存
unless 条件符合则不缓存
sync 是否使用异步模式,默认为false

4.@CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key
keyGenerator key的生成器。key/keyGenerator二选一使用
cacheManager 指定缓存管理器
cacheResolver 指定获取解析器
condition 条件符合则缓存
unless 条件符合则不缓存

5.@CacheEvict
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key
keyGenerator key的生成器。key/keyGenerator二选一使用
cacheManager 指定缓存管理器
cacheResolver 指定获取解析器
condition 条件符合则缓存
allEntries 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
beforeInvocation 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存

3.2.2 CacheConfig

SpringCache是缓存的上层框架,通过它我们可以不需要编写通过redisTemplate写入/查询缓存的操作,但是同样的还是得序列化,具体配置如下:


@Configuration
// 开启 Spring Cache
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

    @Value("${spring.cache.expireTime}")
    // 缓存超时时间
    private int cacheExpireTime;

    /**
     * 配置@Cacheable、@CacheEvict等注解在没有指定Key的情况下,key生成策略
     * 该配置作用于缓存管理器管理的所有缓存
     * 最终生成的key 为 cache类注解指定的cacheNames::类名:方法名#参数值1,参数值2...
     *
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuffer sb = new StringBuffer();
                sb.append(target.getClass().getName());
                sb.append(":");
                sb.append(method.getName());
                sb.append("#");
                for (Object obj : params) {
                    sb.append(obj.toString());
                    sb.append(",");
                }
                return sb.substring(0, sb.length() - 1);
            }
        };
    }


    /**
     * 配置缓存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
            //关键点,spring cache 的注解使用的序列化都从这来,没有这个配置的话使用的jdk自己的序列化,实际上不影响使用,只是打印出来不适合人眼识别
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                // 将 key 序列化成字符串
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 将 value 序列化成 json
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                // 设置缓存过期时间,单位秒
                .entryTtl(Duration.ofSeconds(cacheExpireTime))
                // 不缓存空值
                .disableCachingNullValues();

        return RedisCacheManager.builder(factory)
                .cacheDefaults(cacheConfig)
                .build();
    }
}

4.使用EasyCode初始化数据库对象相关类

为了方便演示与其他章节的测试,因此需要初始化个数据库对象,同时相应编写entity、dao、service、controller类,mapper文件。

创建一个user表,含有id(主键 自增)以及name两个属性。
在这里插入图片描述

如题所示,我们将使用idea的easycode插件,完成entity、dao、service、controller类,mapper文件创建。
在idea中选择Database标签,右键表出现EasyCode,选择GenerateCode。
在这里插入图片描述

然后按照需要配置包名,文件路径,生成文件。
在这里插入图片描述

ok之后,在指定包生成相应文件如下:
在这里插入图片描述

5. 测试应用

5.1 普通web应用测试

为了检验项目能不能用起来,我们一做不做二不休,全套做起来(后面章节方便),其实按照4生成的,我们启动项目,访问localhost:port/swagger-ui.html对接口操作已经是没问题的。

在这里插入图片描述
保存操作成功
在这里插入图片描述
数据库也写入成功,其实idea自带db还是可以用的。
在这里插入图片描述

5.2 应用缓存Redis+SpringCache

5.2.1 SpringCache应用

这个项目目的是使用Redis缓存提高查询效率,降低数据库压力的,提高用户体验的,因此我们需要在目前项目基础上添加相关缓存应用。

由于我们用了SpringCache,我们直接用注解来示范,如何快速完成缓存写入/查询定义。

首先定义对象所在的缓存命名空间,以操作User服务类为例。

@CacheConfig(cacheNames = "ca1")
public class UserServiceImpl implements UserService

此处“ca1”与application.yml里面的spring.cache.cache-names(可以多个name)中对应。

5.2.1.1 查询

使用@Cacheable实现查询通过id先查缓存,第一次查询把结果放到缓存中,后面查询直接查缓存。

 /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    @Cacheable( key = "#id")
    public User queryById(int id) {
        return this.userDao.queryById(id);
    }
5.2.1.2 更新

使用@CachePut实现数据更新刷新缓存。

/**
     * 修改数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    @Override
    @CachePut( key = "#id")
    public User update(User user) {
        this.userDao.update(user);
        return this.queryById(user.getId());
    }
5.2.1.3 删除
 /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    @CacheEvict
    public boolean deleteById(int id) {
        return this.userDao.deleteById(id) > 0;
    }

5.2.2 定义测试接口

定义测试接口:


    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @GetMapping("selectOne")
    public User selectOne(int id) {
        return this.userService.queryById(id);
    }

    /**
     * 插入单条数据
     * @param name
     * @return
     */
    @PostMapping("insertOne")
    public User insert(String name) {
        User user = new User(name);
        return   userService.insert(user);
    }

    /**
     * 删除单条数据
     * @param id
     * @return
     */
    @PostMapping("deleteOne")
    public Boolean delete(int id) {
        return   userService.deleteById(id);
    }

5.2.3 测试接口
5.2.3.1 调用insertOne

调用新增User对象接口,结果如下:
在这里插入图片描述

此时观察Redis数据,没有key为1011的value,因为我们没用加相关注解,因此是没有把1011对象存到缓存,缓存一般是应用在对查询较多的静态对象。所以插入数据的时候可以选择不放缓存。
在这里插入图片描述

5.2.3.2 调用selectOne

接下来对1011数据进行查询,查询结果如下:
在这里插入图片描述

查看redis数据:
在这里插入图片描述

可以看到1011数据。

为了证明是从缓存查出来的,我们直接修改缓存的数据,但是维持数据库的数据。

修改后Redis变成阴天
在这里插入图片描述
数据库还是晴天:
在这里插入图片描述
继续调用查询接口,结果如下:
在这里插入图片描述
这就证明了数据是从缓存里面查出来的。

6.总结

到这里一个相对完整的Redis+SpringBoot+SpringCache基础项目搭建完毕。在搭建基础项目方面好好利用idea本身的功能(模板项目+database)以及插件功能(EasyCode),能够为我们搭建项目提供极大便利,另外就是SpringCache,搭建这个项目不仅仅只用了Redis,也同时使用SpringCache达到快速实现缓存读写对象功能,固然只使用Redis也能达到同样效果,但是开发者为效率而生,能偷懒就偷懒,何况不是野生框架。

有的朋友在redis/cache配置可能会报错:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject

指路:https://blog.csdn.net/qq_28540443/article/details/104731948

推荐阅读: Redis学习之1招雪崩自己的系统附送N招解决雪崩大礼包

发布了32 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_28540443/article/details/104728189