在本Spring Boot 教程中,学习如何从 Spring 框架缓存支持中轻松管理应用程序缓存。Spring 在缓存方面有一些很好的特性,Spring 缓存 API 的抽象使用起来非常简单。

1、缓存是什么?

缓存是一种增强系统性能的机制。它是位于应用程序和持久数据库之间的临时内存。缓存内存存储最近使用的数据项,以便尽可能减少数据库命中次数。

1.1 为什么我们需要缓存?

对应用程序中经常使用的数据进行缓存是提高应用程序性能的一种非常流行的技术。通过缓存,我们将这些频繁访问的数据存储在内存中,以避免每次用户请求数据时触及代价高昂的后端。与从数据库、文件系统或其他服务调用等存储中获取数据相比,从内存中访问数据总是更快。

1.2 什么数据需要被缓存?

这主要是关于应该驻留在缓存中并经历缓存生命周期的数据类型的主观决定。在不同的场景和需求中,我们可以容忍陈旧数据的时间是不同的。

所以缓存的候选对象在每个项目中都是不同的,这些只是缓存的几个例子:

  • 电子商务商店可提供的产品列表
  • 任何不经常更改的主数据
  • 任何经常使用的数据库读查询,其结果至少在特定时间段内不会在每次调用中改变。

2、缓存的类型

通常,可以看到以下类型的缓存。

2.1 内存缓存

这是最常用的领域,缓存被广泛用于提高应用程序的性能。内存中的缓存(如 MemcachedRadis)是应用程序和数据存储之间的键值存储。由于数据保存在 RAM 中,它比数据存储在磁盘上的典型数据库要快得多。

RAM 比磁盘更有限,因此缓存失效算法(如最近最少使用(LRU))可以帮助使“冷”条目失效,并在RAM中保留“热”数据。Memcached 是内存缓存,Redis 更先进,它允许我们备份和恢复功能,它也是分布式缓存工具,我们可以在分布式集群中管理缓存。

2.2 数据库缓存

数据库通常在默认配置中包含某种级别的缓存,并针对通用用例进行了优化。针对特定的使用模式调整这些设置可以进一步提高性能。这方面的一个流行方法是 Hibernate 或任何 ORM 框架的一级缓存。

2.3 Web 服务缓存

反向代理和像 Varnish 这样的缓存可以直接提供静态和动态内容。Web 服务器还可以缓存请求,返回响应而不必联系应用服务器。在今天的 API 时代,如果我们想在 web 服务器级别缓存 API 响应,这个选项是可行的。

2.4 CDN 缓存

缓存可以位于客户端(操作系统或浏览器)、服务器端或不同的缓存层。

3、Spring boot 缓存注解

Spring框架为不同的缓存提供程序提供了缓存抽象api。API的使用非常简单,但功能非常强大。今天我们将看到基于注释的Java缓存配置。注意,我们也可以通过XML配置实现类似的功能。

3.1. @EnableCaching

它启用了 Spring 注释驱动的缓存管理功能。在 spring boot 项目中,我们需要将它添加到带有@SpringBootApplication 注释的引导应用程序类中。Spring 提供一个并发 hashmap 作为默认缓存,但我们也可以覆盖 CacheManager 来轻松注册外部缓存提供者。

3.2 @Cacheable

它用于方法级别,让 spring 知道方法的响应是可缓存的。Spring 管理此方法对注释属性中指定的缓存的请求/响应。例如,@Cacheable ("cache-name1"" cache-name2 ")。

@Cacheable 注释有更多选项。比如我们可以从方法的请求中指定缓存的键。如果没有指定,spring 将使用所有的类字段并使用这些字段作为缓存键(主要是 HashCode )来维护缓存,但我们可以通过提供键信息来覆盖这种行为。

@Cacheable(value="books", key="#isbn")
public Book findStoryBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
 
@Cacheable(value="books", key="#isbn.rawNumber")
public Book findStoryBook (ISBN isbn, boolean checkWarehouse, boolean includeUsed)
 
@Cacheable(value="books", key="T(classType).hash(#isbn)")
public Book findStoryBook (ISBN isbn, boolean checkWarehouse, boolean includeUsed)

我们也可以使用条件缓存。例如,

@Cacheable(value="book", condition="#name.length < 50")
public Book findStoryBook (String name)

3.3. @CachePut

有时我们需要手动操作缓存,以便在方法调用之前放置(更新)缓存。这将允许我们更新缓存,也将允许执行方法。该方法将始终被执行,其结果将被放入缓存中(根据 @CachePut 选项)。

它支持与 @Cacheable 相同的选项,应该用于缓存填充而不是方法流优化。

注意,通常不鼓励在同一方法上使用 @CachePut@Cacheable 注释,因为它们有不同的行为。后者会通过使用缓存跳过方法执行,而前者会强制执行以便执行缓存更新。
这将导致意料之外的行为,除了特定的极端情况(例如具有相互排斥的条件的注释),应该避免这样的声明。

3.4. @CacheEvict

当我们需要驱逐(删除)以前加载的主数据缓存时,使用它。当执行带有 CacheEvict 注释的方法时,它将清除缓存。

我们可以在这里指定 key 来删除缓存,如果我们需要删除缓存中的所有条目,那么我们需要使用allEntries=true。当需要清除整个缓存区域时,这个选项非常方便——而不是删除每个条目(这将花费很长时间,因为它效率低),所有条目都在一个操作中删除。

3.5. @Caching

当我们同时需要 CachePutCacheEvict 时,这个注释是必需的。

4. 如何用spring Boot 注册一个缓存引擎

Spring 引导提供了与以下缓存提供程序的集成。如果默认选项存在于类路径中,并且我们在 Spring 引导应用程序中通过 @EnableCaching 启用了缓存,那么 Spring 引导将使用默认选项自动配置。

  • JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
  • EhCache 2.x
  • Hazelcast
  • Infinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple cache

我们可以在 Spring 引导中通过覆盖缓存提供程序的特定设置来覆盖特定的缓存行为,例如:

spring.cache.infinispan.config=infinispan.xml

5、Spring boot 缓存示例

在这个 spring boot 缓存配置示例中,我们将看到如何在 spring 引导中启用默认缓存,以及如何为其中一个业务方法启用缓存。最后,我们将测试应用程序在重复调用相同方法时的性能。

我们将通过使用 Thread.sleep() 方法来模拟实际方法调用中的延迟,以感受缓存的效果。因此,让我们遵循创建项目和测试的简单步骤。

5.1 创建一个 Spring Boot 项目

创建一个简单的 spring boot 项目,名为 spring-cache,带有 spring-boot-web dependency,用于在 web 服务器中托管。

为此,我们需要访问 https://start.spring.io/,为 maven 提供坐标并选择依赖项。下载包含框架项目的 zip 文件。然后,在适当的文件夹中解压后,我们需要在 eclipse 中导入它。执行初始的 mvn 清理安装,将所有必需的依赖项下载到本地存储库。

spring boot jar 一直查询内存一直加 spring boot 内存缓存_spring boot

5.2 创建一个 HTTP GET RESTAPI

创建一个REST服务,它将是一个使用GET请求的搜索服务。我们的主要目标是在服务层中缓存方法的响应,在服务层中我们将引入一个有意的延迟来模拟实际的后端服务调用,以获得结果。在第一次调用中,响应将被延迟,因为我们将在应用程序中有一些模拟延迟,但在后续调用中,我们将得到更快的响应。

Student.java

package com.example.springcache.domain;
 
public class Student {
 
  String id;
  String name;
  String clz;
 
  public Student(String id, String name, String clz) {
    super();
    this.id = id;
    this.name = name;
    this.clz = clz;
  }
 
  //Setters and getters
}

StudentService.java

package com.example.springcache.service;
 
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.example.springcache.domain.Student;
 
@Service
public class StudentService 
{
  @Cacheable("student")
  public Student getStudentByID(String id) 
  {
    try
    {
      System.out.println("Going to sleep for 5 Secs.. to simulate backend call.");
      Thread.sleep(1000*5);
    } 
    catch (InterruptedException e) 
    {
      e.printStackTrace();
    }
 
    return new Student(id,"Sajal" ,"V");
  }
}

StudentController.java

package com.example.springcache.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.example.springcache.domain.Student;
import com.example.springcache.service.StudentService;
 
@RestController
public class StudentController 
{
 
  @Autowired
  StudentService studentService;
 
  @GetMapping("/student/{id}")
  public Student findStudentById(@PathVariable String id) 
  {
    System.out.println("Searching by ID  : " + id);
 
    return studentService.getStudentByID(id);
  }
}

注意:

  • 服务层方法使用 @Cacheable("student") 进行注释,如上所述,该注释在此特定方法中启用缓存,缓存名称为 student
  • getStudentByID() 方法中,我们使用 Thread.sleep(1000*5) 故意延迟 5 秒。这只是为了了解响应是来自缓存还是真正的后端。

5.3 使用 Spring 管理缓存

为此,我们只需要在Spring Boot应用程序类中添加@EnableCaching注释。

package com.example.springcache;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
 
@SpringBootApplication
@EnableCaching
public class SpringCacheApplication {
 
  public static void main(String[] args) {
    SpringApplication.run(SpringCacheApplication.class, args);
  }
}

5.4 Demo

现在我们可以很好地测试 spring 缓存的默认缓存行为了。我们已经添加了所需的配置,有了 spring boot ,现在就容易多了。

要进行测试,只需通过 $ vmn clean install命令再次构建项目,然后从命令行java命令运行应用程序,或者从IDE运行 SpringCacheApplication。它将在本地主机 8080 端口启动应用程序。

要测试,请访问url : http://localhost:8080/student/1

你会得到一个Student对象的 JSON 响应。注意,第一次响应至少需要5秒才能响应,然后相同 url 的后续响应将更快。如果理解差异有困难,可以更改服务类中的延迟时间。

现在将 url 更改为通过 http://localhost:8080/student/2 获得 Student id 2,您将再次经历延迟,但在后续调用中,响应将从 Cache 中提供。

这是我系统里关于这个的最后几行日志。当真正的服务被调用的时候,我要去睡5秒。模拟后端调用。在后续的调用中,我没有得到那个日志,这意味着从缓存中提供响应。

Searching by ID  : 1
Going to sleep for 5 Secs.. to simulate backend call.
 
Searching by ID  : 1
Searching by ID  : 1
Searching by ID  : 1
Searching by ID  : 1
Searching by ID  : 1
 
Searching by ID  : 2
Going to sleep for 5 Secs.. to simulate backend call.
 
Searching by ID  : 2
Searching by ID  : 2

6 Spring boot cache 总结

最后需要注意的是,今天我们已经看到了 spring 框架在特定于应用程序缓存的缓存领域提供了什么。我们还看到了 spring 中支持该功能的注释。

我希望本教程对你有用。在这篇文章中,我们使用了回退缓存提供程序,即后台的 ConcurrentHashMap。下一步是配置其他支持的缓存引擎,如 Redis, Ehcache 等。

参考文章: