Spring Boot 技术探索

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".

28、Spring Boot之Cache缓存的使用

平台环境:

名称

版本号

Mac OS X

10.15.1

JDK

1.8.0_201

Apache Maven

3.6.0

IntelliJ IDEA

2019.2.4 (Ultimate Edition)

Spring Boot

2.2.1.RELEASE

Redis

stable 5.0.6 (bottled), HEAD

 

  什么是Spring Cache?

  Spring Cache是Spring专门用于缓解数据库压力的解决方案。

 

pom.xml中引入依赖,其中最重要的依赖是:spring-boot-starter-cache

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <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-cache</artifactId>
    </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>
</dependencies>

 

应用程序主函数增加开启缓存注解@EnableCaching

package com.example.demo;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;


@EnableCaching
@SpringBootApplication
public class DemoApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(DemoApplication.class, args);
    }
}

 

application.properties中加入Redis的相关配置

# REDIS
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0

到此,Spring Cache的基础工作已经准备好,接下来就可以使用@Cacheable、@CachePut、@CacheEvict标签来实现缓存功能了。

 

新建测试控制器UserController

package com.example.demo.web;


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.RestController;




@RestController
public class UserController
{
    @RequestMapping("/hello")
    @Cacheable(value = "helloCache")
    public String hello(String name)
    {
        System.out.println("没有走缓存!");
        return "hello " + name;
    }


    @RequestMapping("/condition")
    @Cacheable(value = "condition", condition = "#name.length() <= 4")
    public String condition(String name)
    {
        System.out.println("没有走缓存!");
        return "hello " + name;
    }


    @RequestMapping("/getUser")
    @Cacheable(value = "usersCache", key = "#nickname")
    public String getUser(String nickname)
    {
        System.out.println("执行了数据库操作");
        return nickname;
    }


    @RequestMapping("/getPutUser")
    @CachePut(value = "usersCache", key = "#nickname")
    public String getPutUser(String nickname)
    {
        System.out.println("执行了数据库操作");
        return nickname + "_new";
    }


    @RequestMapping("/allEntries")
    @CacheEvict(value = "usersCache", allEntries = true)
    public String allEntries(String nickname)
    {
        String msg = "执行了allEntries";
        System.out.println(msg);
        return msg;
    }


    @RequestMapping("/beforeInvocation")
    @CacheEvict(value = "usersCache", allEntries = true, beforeInvocation = true)
    public void beforeInvocation()
    {
        throw new RuntimeException("test beforeInvocation");
    }
}

 

测试1:

访问http://localhost:8080/hello?name=codelabx

控制台显示

没有走缓存!

再次刷新刚才的地址

控制台无任何文字输出。说明没有执行hello方法,而是从缓存中加载的。

 

测试2:

访问http://localhost:8080/condition?name=codelabx

控制台显示

没有走缓存!

再次刷新刚才的地址

控制台显示

没有走缓存!
没有走缓存!

再次刷新刚才的地址

控制台显示

没有走缓存!
没有走缓存!
没有走缓存!

说明,因为传入的name参数不满足condition条件中的#name.length() <= 4,所以缓存不生效。

继续访问http://localhost:8080/condition?name=code

控制台显示

没有走缓存!

再次刷新刚才的地址

控制台无任何文字输出。说明没有执行condition方法,而是从缓存中加载的。

 

测试3:

访问http://localhost:8080/getUser?nickname=abcde

控制台显示

执行了数据库操作

再次刷新刚才的地址

控制台无任何文字输出。说明没有执行getUser方法,而是从缓存中加载的。这里就埋下一个严重的问题,缓存数据与数据库数据开始不一致。

访问http://localhost:8080/getPutUser?nickname=abcde

控制台显示

执行了数据库操作

再次刷新刚才的地址

控制台显示

执行了数据库操作
执行了数据库操作

再次刷新刚才的地址

控制台显示

执行了数据库操作
执行了数据库操作
执行了数据库操作

说明每次访问这个地址都执行了getPutUser方法,并且@CachePut注解的getPutUser方法会自动更新缓存。

访问http://localhost:8080/getUser?nickname=abcde

页面上显示

abcde_new

控制台无任何文字输出。说明没有执行getUser方法,而是从缓存中加载的。

说明缓存已被更新。

 

测试4:

访问http://localhost:8080/allEntries

控制台显示

执行了allEntries

访问http://localhost:8080/getUser?nickname=abcde

控制台显示

执行了数据库操作

说明缓存已经被清空,从而执行了getUser方法。

 

测试5:

上面的测试中,清空缓存的@CacheEvict标签默认会在注解的方法发生报错的情况时,不执行清空缓存操作。

通过配置参数beforeInvocation = true,可以强制在报错的情况下也清空缓存。

访问http://localhost:8080/beforeInvocation

访问http://localhost:8080/getUser?nickname=abcde

控制台显示

执行了数据库操作

说明缓存已经被清空,从而执行了getUser方法。

 

 

总结一下:

@Cacheable 作用和配置方法

主要针对方法配置,能够根据方法的请求参数对其结果进行缓存:

主要参数

解释

举例

value 

缓存的名称,在 spring 配置文件中定义,必须指定至少一个 

如 @Cacheable(value="mycache") 或者 @Cacheable(value={"cache1","cache2"} 

key 

缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 

如 @Cacheable(value="testcache",key="#userName") 

condition 

缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 

如 @Cacheable(value="testcache",condition="#userName.length()>2") 

 

@CachePut 作用和配置方法

作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法的调用。

主要参数

解释

举例

value 

缓存的名称,在 spring 配置文件中定义,必须指定至少一个 

如 @Cacheable(value="mycache") 或者 @Cacheable(value={"cache1","cache2"} 

key 

缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 

如 @Cacheable(value="testcache",key="#userName") 

condition 

缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 

如 @Cacheable(value="testcache",condition="#userName.length()>2") 

 

@CacheEvict 作用和配置方法

主要针对方法配置,能够根据一定的条件对缓存进行清空。

主要参数

解释

举例

value 

缓存的名称,在 spring 配置文件中定义,必须指定至少一个 

如 @CachEvict(value="mycache") 或者 @CachEvict(value={"cache1","cache2"} 

key 

缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 

如 @CachEvict(value="testcache",key="#userName") 

condition 

缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 

如 @CachEvict(value="testcache",condition="#userName.length()>2") 

allEntries 

是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 

如 @CachEvict(value="testcache",allEntries=true) 

beforeInvocation 

是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 

如 @CachEvict(value="testcache",beforeInvocation=true) 

 

 

Bootstrap Thumbnail Second
MySQL

MySQL is the world's most popular open source database.

GO

Bootstrap Thumbnail Third
算法基础

本书介绍了什么是计算机算法,如何描述它们,以及如何来评估它们。

GO