SpringBoot与Spring Framework提供的缓存抽象

news2024/10/7 10:15:14

目录

缓存

项目总结

新建一个SpringBoot项目

pom.xml

application.properties

CacheConfig

Book

BookRepository接口

BookService服务类

BookController控制器

SpringbootCacheApplication启动类

启动项目,使用Postman测试


参考博文:

  • 1、使用到了JPA,建议先看看SpringBoot整合JPA保存数据:http://t.csdnimg.cn/zJfHv

缓存

  • Spring Boot 实际上是建立在 Spring Framework 的基础之上的,并且提供了对 Spring Framework 中各种功能的自动化配置和简化,包括缓存抽象。

  • Spring Framework 提供了一套强大的缓存抽象,包括 @Cacheable@CachePut@CacheEvict 等注解,以及 CacheManager 接口和相关的实现类,用于管理缓存的操作和配置。这些缓存抽象使得开发人员可以方便地在应用程序中集成各种缓存解决方案,并使用统一的方式来进行缓存操作。

  • 缓存机制:Spring的缓存抽象是作用于方法级别的,在每次调用一个目标方法时,缓存抽象都会应用一个缓存行为来检查是否已经为给定参数调用了该方法。如果已经调用该方法,则返回缓存的结果,而不会再调用实际的方法;如果还没有调用该方法,则会调用目标方法,并将方法返回的结果缓存,同时向用户返回该结果。这种缓存机制适用于对给定参数始终返回相同结果的方法
  • 缓存抽象提供了5个缓存注解:
@Cacheable根据方法参数将方法结果保存到缓存中
@CachePut执行方法,同时更新缓存 
@CacheEvict清空缓存 
@Caching重新组合要应用于某个方法的多个缓存操作
@CacheConfig在类级别共享一些共同的缓存相关配置

项目总结

  • 引入依赖
  • 在 application.properties 或 application.yml 中配置数据库连接,所选缓存提供商的相关信息
  • 在 Spring Boot 应用程序的主类上,添加 @EnableCaching 注解来启用缓存支持
  • 在需要进行缓存的方法上添加 @Cacheable@CachePut@CacheEvict 等注解,来控制缓存的行为。

新建一个SpringBoot项目

引入依赖:

  • Lombok
  • Spring Web
  • Spring Data JPA
  • MySQL Driver
  • Spring Cache Abstraction

项目结构:

pom.xml

<?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.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.study</groupId>
	<artifactId>springboot_cache</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot_cache</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<version>8.0.32</version>
			<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>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

application.properties

# test为数据库名
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate配置属性,设置自动根据实体类创建,更新和验证数据库表结构
spring.jpa.properties.hibernate.hbm2ddl.auto=update
# Hibernate 将会使用 MySQL 5 InnoDB 存储引擎的方言来生成针对 MySQL 数据库的 SQL 查询和语句。
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
# 将运行期生成的SQL语句输出到日志以供调试
spring.jpa.show-sql=true

CacheConfig

  • 因为缓存本质上是键-值存储,所以每次调用缓存的方法都需要转换成合适的键来进行缓存访问。
  • 默认的键生成策略:
    • 如果缓存方法没有参数,则使用SimpleKey.EMPTY作为键
    • 如果有多个参数,则返回包含所有参数的SimpleKey
package com.study.springboot_cache.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 这是一个自定义的缓存键生成器(KeyGenerator)配置类。
 * 在这个配置类中,你重写了 CachingConfigurerSupport 类的 keyGenerator() 方法,
 * 用于生成缓存的键,避免出现重复的键。
 */
@Configuration
public class CacheConfig extends CachingConfigurerSupport {

    @Bean
    @Override //alt+insert
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            /**
             * 将目标对象的类名、方法名和参数数组拼接起来作为键的生成规则,以确保缓存的唯一性
             */
            @Override
            public Object generate(Object target, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName())
                        .append(".")
                        .append(method.getName())
                        .append(Arrays.toString(objects));
                return sb.toString();
            }
        };
    }
}

Book

package com.study.springboot_cache.entity;

import lombok.Data;
import lombok.ToString;

import javax.persistence.*;
import java.time.LocalDate;

@Data
@ToString
@Entity
@Table(name = "books")
public class Book {

    //主键自动增长
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;//主键ID
    private String title;//书名
    private String author;//作者
    private String bookConcern;//出版社
    private LocalDate publishDate;//出版日期
    private Float price;//价格
}

BookRepository接口

  • JpaRepository 接口:是 Spring Data JPA 提供的一个泛型接口,用于简化数据访问层的开发。它提供了许多内置的方法,用于对实体类进行常见的 CRUD 操作
package com.study.springboot_cache.repository;

import com.study.springboot_cache.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book,Integer> {
    Book getById(Integer id);
}

BookService服务类

  • 对于DAO类来说,通常是一个方法完成一次SQL访问操作,粒度比较细,对于一次前端请求来说,可能需要调用多个DAO类方法来得到结果,而服务层就是负责组合这些DAO方法的,因此,在服务层的类方法缓存结果是比较合适的
  • SpEL(Spring Expression Language,Spring表达式语言),可以通过SpEL来选择合适的参数生成键
package com.study.springboot_cache.service;

import com.study.springboot_cache.entity.Book;
import com.study.springboot_cache.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @CacheConfig(cacheNames = "book")在类级别共享一些共同的缓存相关配置
 * 设置缓存的名称为book
 */
@Service
@CacheConfig(cacheNames = "book")
public class BookService {

    @Autowired
    private BookRepository bookRepository;

    /**
     * @Cacheable根据方法参数将方法结果保存到缓存中,在后续使用相同参数调用方法时,
     * 会直接返回缓存中的值,而不再调用目标方法
     */
    @Cacheable
    public Book getBookById(Integer id){
        System.out.println("getBookById: "+id);
        return bookRepository.getById(id);
    }

    /**
     * @CachePut(key = "#result.id")调用方法以更新缓存
     * key = "#result.id": 缓存的key,其值为SpEL表达式
     */
    @CachePut(key = "#result.id")
    public Book saveBook(Book book){
        System.out.println("saveBook: "+book);
        book = bookRepository.save(book);
        return book;
    }

    @CachePut(key = "#result.id")
    public Book updateBook(Book book){
        System.out.println("updateBook: "+book);
        book = bookRepository.save(book);
        return book;
    }

    /**
     * @CacheEvict(beforeInvocation = true)删除缓存中过时或未使用的数据
     * beforeInvocation = true: 在方法执行前删除
     */
    @CacheEvict(beforeInvocation = true)
    public void deleteBook(Integer id){
        System.out.println("deleteBook: "+id);
        bookRepository.deleteById(id);
    }
}

BookController控制器

package com.study.springboot_cache.controller;

import com.study.springboot_cache.entity.Book;
import com.study.springboot_cache.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/book")
public class BookController {

    @Autowired
    private BookService bookService;

    @PostMapping
    public String saveBook(@RequestBody Book book){
        Book resultBook = bookService.saveBook(book);
        return resultBook.toString();
    }

    @GetMapping("/{id}")
    public String getBookById(@PathVariable Integer id){
        Book resultBook = bookService.getBookById(id);
        return resultBook.toString();
    }

    @PutMapping
    public String updateBook(@RequestBody Book book){
        Book resultBook = bookService.updateBook(book);
        return resultBook.toString();
    }

    @DeleteMapping
    public String deleteBook(@PathVariable Integer id){
        bookService.deleteBook(id);
        return "删除成功!";
    }
}

SpringbootCacheApplication启动类

package com.study.springboot_cache;

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

@SpringBootApplication
@EnableCaching //启用缓存
public class SpringbootCacheApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootCacheApplication.class, args);
	}

}

启动项目,使用Postman测试

1、向表中添加数据

2、查询数据

第一次查询时,会调用方法,在控制台打印出SQL语句;

第二次使用相同参数查询时,使用缓存中的数据,不会调用方法,控制台没有输出

3、 更改数据

更改数据,再查询两次,第二次查询也是使用缓存中数据,不调用方法,控制台不打印SQL语句

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1716820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

短视频脚本创作的五个方法 沈阳短视频剪辑培训

说起脚本&#xff0c;我们大概都听过影视剧脚本、剧本&#xff0c;偶尔可能在某些综艺节目里听过台本。其中剧本是影视剧拍摄的大纲&#xff0c;用来指导影视剧剧情的走向和发展&#xff0c;而台本则是综艺节目流程走向的指导大纲。 那么&#xff0c;短视频脚本是什么&#xf…

服务器端请求伪造--SSRF

SSRF 简介 ##SSRF定义 SSRF(Server-Side Request Forgery:服务器端请求伪造)是一种由 攻击者构造形成&#xff0c;由服务端发起请求 的一个安全漏洞。一般情况下&#xff0c;SSRF攻击的目标是从 外网无法访问的内部系统&#xff08;正是因为它是由服务端发起的&#xff0c;所…

FineReport帆软设计器,远程连接服务器

FineReport报表工具一款纯Java编写的企业级web报表软件工具。它能够全面支持主流的B/S架构以及传统的C/S架构&#xff0c;部署方式简单而灵活. 需要使用FineReport帆软设计器&#xff0c;配置远程服务器的方式如下&#xff1a; 1、打开帆软设计器&#xff0c;点击文件&#x…

工程项目管理系统的Java实现:高效协同与信息共享

在当今的工程领域&#xff0c;项目管理的高效协同和信息共享是提升工作效率、降低成本的关键。本文将向您介绍一款基于Java技术构建的工程项目管理系统&#xff0c;该系统采用前后端分离的先进技术框架&#xff0c;功能全面&#xff0c;能够满足不同角色的需求&#xff0c;从项…

中英文语音合成芯片(TTS芯片)WT3000T8-在ETC上的应用案例

一&#xff1a;开发背景&#xff1a; 随着智能化浪潮的推进&#xff0c;ETC&#xff08;电子不停车收费&#xff09;系统已逐渐成为现代交通的重要组成部分。在这一背景下&#xff0c;唯创知音自主研发的WT3000T8中文转语音合成芯片&#xff08;TTS芯片&#xff09;以其卓越的性…

基于TAE的数字钥匙自动化测试解决方案

方案概述 在汽车发展和用户需求的推动下&#xff0c;汽车钥匙开始从传统的机械钥匙向数字化、智能化方向发展。目前常见的数字钥匙集成了蓝牙、NFC、UWB等技术实现了移动设备与车端的通信&#xff0c;可以帮助用户便捷的实现车辆功能控制。随着数字钥匙的广泛应用&#xff0c;相…

振弦式位移计在各类工程中的应用

振弦式位移计&#xff0c;作为一种高精度、高可靠性的测量工具&#xff0c;在各类工程中发挥着至关重要的作用。它通过测量弦的振动频率来间接得出结构的位移变化&#xff0c;为工程师和科学家们提供了精确的数据支持&#xff0c;从而确保工程的安全性和稳定性。 点击输入图片描…

YOLOv5改进 | 注意力机制 | 添加双重注意力机制 DoubleAttention【附代码/涨点能手】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 在图像识别中&#xff0c;学习捕捉长距离关系是基础。现有的CNN模型通常通过增加深度来建立这种关系&#xff0c;但这种形式效率极低。因此&…

Hudi之数据读写探究

Hudi之数据读写深入探究 1. Hudi数据写入 1-1. 写操作 Hudi数据湖中的数据更新、插入和删除操作&#xff0c;是一个基于Apache Hadoop的库&#xff0c;为数据湖提供了一种有效的方法来处理更新和增量数据&#xff0c;并支持基于时间的快照和增量数据处理。Hudi支持三种主要的…

Linux源码编译安装MySQL + Qt连接MySQL

一、准备工作 1. 编译环境&#xff1a; 银河麒麟V10 飞腾D2000 CPU 2. 下载MySQL源码 这里编译的是5.7.44版本&#xff0c;带Boost库&#xff0c;这是官网的下载地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions) 3. 解压压缩包 tar -zxvf mys…

Guns框架:基于主流技术Spring Boot2 + Vue3 + Antd Vue的现代Java应用开发新纪元

Guns框架&#xff1a;基于主流技术Spring Boot2 Vue3 Antd Vue的现代Java应用开发新纪元 摘要&#xff1a;随着信息技术的飞速发展&#xff0c;软件开发框架在提升开发效率、降低成本方面扮演着至关重要的角色。Guns框架&#xff0c;作为一个现代化的Java应用开发框架&#x…

vue3 组件的动态渲染 <component :is=“componentTag“ />

1、动态渲染组件 <component :is""></component> 通过isShow来切换显示A、B组件 首先创建父组件.vue文件和两个子组件A、B文件&#xff0c;并引入。 template&#xff1a; <div><h3>我是父组件dynamicComp.vue</h3><button clic…

firebase如何自定义上传日志

我们可以很轻松的得到2个代码&#xff1a; Firebase.crashlytics.log(str) Firebase.crashlytics.recordException(ex)这就是firebase提供的自定义日志和excption上传的方法。 但是如果你认为log函数调用后&#xff0c;直接就能查看到日志就错了。 我们在这个page是找不到日志…

端午节粽子龙舟主题互动趣味小游戏效果是什么

端午三天乐&#xff0c;无论节日当天还是之前&#xff0c;行业商家都可以自己的品牌为主借势营销&#xff0c;趣味活动形式玩法和内容呈现达成多种效果&#xff0c;品牌传播、公众号涨粉、线下互动、商品促销、用户促活等。 在【雨科】平台拥有多款端午节互动小游戏类型&#…

操作系统真象还原:编写MBR主引导记录,让我们开始掌权

第2章-编写MBR主引导记录&#xff0c;让我们开始掌权 这是一个网站有所有小节的代码实现&#xff0c;同时也包含了Bochs等文件 在开机的一瞬间&#xff0c;也就是接电的一瞬间&#xff0c; CPU 的 CS: ip 寄存器被强制初始化为 0XF000: 0XFFF0。由于开机的时候处于实模式&…

第一课、Power BI 集成Python

1&#xff0c;下载安装python Python软件地址&#xff1a;Welcome to Python.org 双击自定义安装指定位置并勾选配置环境变量。 后续一直往下&#xff0c;安装完成。 检验是否成功&#xff0c;在cmd命令窗口下输入python 即可看到版本。 安装 pip install pandas 和 pip…

win10双网卡如何同时上内网和外网?

win10双网卡如何同时上内网和外网? Chapter1 win10双网卡如何同时上内网和外网?Chapter2 网络基础--win10双网卡设置成访问不同的网络 Chapter1 win10双网卡如何同时上内网和外网? 原文链接&#xff1a;https://www.jb51.net/os/win10/806585.html 场景&#xff1a;很多办…

使用递归形式以及迭代形式实现树的前中后序遍历

相信大家对于二叉树的遍历并不陌生&#xff0c;对于二叉树的递归遍历我们也可以信手拈来。但是如果让我们将二叉树修改成为非递归的形式呢&#xff1f;是不是有点疑惑了&#xff1f;那么本次博客我们就来梳理一下二叉树的非递归遍历。 由于递归遍历二叉树的代码以及逻辑都很简单…

LeetCode 63.不同路径Ⅱ

思路&#xff1a; 在有障碍物的地方增加一个判断即可 class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int dp[105][105];int mobstacleGrid.size();int nobstacleGrid[0].size();for(int i0;i<m;i){for(int j0…

拓数派与浙江平数举行「政务数据服务产品合作开发」签约仪式

3月14日&#xff0c;杭州拓数派科技发展有限公司&#xff08;以下简称“拓数派”&#xff09;与浙江平数科技有限公司&#xff08;以下简称“浙江平数”&#xff09;举行了关于政务数据服务产品合作开发的签约仪式。在嘉兴平湖市政务服务管理办公室党委副书记、主任&#xff0c…