最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot 緩存源碼分析

 更新時間:2025年12月16日 08:56:54   作者:guslegend  
JSR107是Java規(guī)范請求,定義了緩存接口,如CachingProvider、CacheManager、Cache等,SpringCache是Spring對緩存的抽象,通過@EnableCaching開啟緩存注解,使用@Cacheable、@CachePut、@CacheEvict等注解進(jìn)行緩存操作,本文介紹SpringBoot 緩存的相關(guān)知識,感興趣的朋友一起看看

JSR107

在我們了解SpringBoot緩存深入的時候,我們首先需要了解JSR107。

JSR:

  • 是Java Specification Requset 的縮寫,Java規(guī)范請求;
  • 其是Java提供的一個接口規(guī)范,類似于jdbc規(guī)范,但是沒有具體的實現(xiàn),具體的實現(xiàn)就是redis等這些緩存。

JSR107核心接口:

  • CachingProvider(緩存提供者):創(chuàng)建,配置,獲取,管理和控制多個CacheManager;
  • CacheManager(緩存管理器):創(chuàng)建,配置,獲取,管理和控制多個唯一命名的Cache,Cache存在于CacheManager的上下文中,一個CacheManager僅對應(yīng)一個CachingProvider;
  • Cache(緩存):是由CacheManager管理的,CacheManager僅對應(yīng)一個Cache的生命周期,Cache存在于CacheManager的上下文中。類似于map的數(shù)據(jù)結(jié)構(gòu),并臨時存儲以key為索引的值。一個Cache僅被一個CacheManager所擁有;
  • Entry(緩存鍵值對):是一個存儲在Cache中的key-value對;
  • Expiry(緩存時效):每一個存儲在Cache中的條目都有一個定義的有效期。一旦超過這個時間,條目就會自動過期,過期后,條目將不可用訪問,更新和刪除操作。緩存有效期可以通過ExpiryPolicy設(shè)置。

要使用JSR107需要導(dǎo)入相關(guān)的maven依賴

<dependency>
  <groupId>javax.cache</groupId>
  <artifactId>cache-api</artifactId>
</dependency>

Spring的緩存抽象

Spring Cache:

  • 只負(fù)責(zé)維護(hù)抽象層,具體的實戰(zhàn)由自己的技術(shù)選型來決定;
  • 將緩存處理和緩存技術(shù)解除耦合;
  • 每次調(diào)用緩存功能方法時,Spring會檢查指定參數(shù)的目標(biāo)方法是否已經(jīng)被調(diào)用;
  • 如果有就直接從緩存中獲取方法調(diào)用后的結(jié)果,如果沒有就調(diào)用方法并緩存結(jié)果返回給用戶,下次直接從緩存中獲取即可。

當(dāng)我們使用Spring Cache緩存抽象的時候,我們需要關(guān)注兩點:

  1. 確定那些方法需要被緩存;
  2. 緩存策略。

Spring 緩存使用

重要概念和緩存注解

在正式開始進(jìn)入SpringCache實戰(zhàn)之間,我們需要先了解一下Spring Cache 的緩存注解和幾個重要概念。

概率/注解

作用

Cache

緩存接口,定義緩存操作。實現(xiàn)有RedisCache等

CacheManaer

緩存管理器,管理各種緩存(Cache)組件

@Cacheable

主要針對方法配置,能夠根據(jù)方法的請求參數(shù)對其結(jié)果進(jìn)行緩存

@CacheEvict

清空緩存

@CachePut

保證方法被調(diào)用,又希望結(jié)果被緩存

@EnableCaching

開啟緩存注解

keyGenerator

緩存數(shù)據(jù)時key生成

serialize

緩存數(shù)據(jù)時value序列化策略

說明:

  1. @Cacheable標(biāo)注在方法上,表示該方法的結(jié)果需要被緩存起來;
  2. 緩存的鍵由keyGenerator的策略決定,緩存的值的形式是由serialize決定(序列化還是json格式);
  3. 標(biāo)注上該注解之后,在緩存時效內(nèi)再次調(diào)用該方法將不會調(diào)用方法本身而是直接從緩存中獲取結(jié)果;
  4. @CachePut也是標(biāo)注在方法上,和@Cacheable相似也會將方法的返回值存儲起來,不同的是標(biāo)注@CachePut的方法每次都會被調(diào)用,而且每次都會將結(jié)果緩存起來,適用對象的更新。

環(huán)境搭建

首先我們要創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu)

SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `department_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `gender` int(11) DEFAULT NULL,
  `d_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后添加maven依賴

<?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 http://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.9.RELEASE</version>
        <relativePath/>
        <!--  lookup parent from repository  -->
    </parent>
    <groupId>com.guslegend</groupId>
    <artifactId>SpringCacheDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <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.4</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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </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>

接下來生成實體類,controller層,service層,mapper層。

還有我們需要開啟mybatis的駝峰映射,和配置日志級別,方便我們查看sql語句,看緩存是否生效。

緩存初體驗

首先我們需要再啟動類上添加@EnableCaching,允許使用緩存。

Cacheable

屬性名

描述

cacheNames/value

指定緩存的名字,用于區(qū)分不同緩存組件;可通過該屬性指定緩存鍵值,將一個緩存鍵值分到多個緩存中。

key

緩存數(shù)據(jù)時的 key 值,默認(rèn)使用方法參數(shù)值,支持 SpEL 表達(dá)式生成 key。

keyGenerator

緩存的生成策略,與 key 功能一致,支持自定義生成規(guī)則。

cacheManager

定義緩存管理器(如 ConcurrentHashMap、Redis 等)。

cacheResolver

與 cacheManager 功能一致,二者選其一。

condition

指定緩存的條件,滿足條件時才緩存(如#id>0

表示入?yún)⒋笥?0 時緩存),支持 SpEL 表達(dá)式。

unless

緩存后判斷條件,滿足時不緩存(如#result==null

表示結(jié)果為 null 時不緩存),支持 SpEL 表達(dá)式。

sync

是否使用異步模式進(jìn)行緩存。

注意:

  • 即滿足condition又滿足unless條件的也不進(jìn)行緩存;
  • 使用異步模式進(jìn)行緩存時(sync=true):unless條件將不被支持。

名字

位置

描述

示例

methodName

root object

當(dāng)前被調(diào)用的方法名

#root.methodName

method

root object

當(dāng)前被調(diào)用的方法

#root.method.name

target

root object

當(dāng)前被調(diào)用的目標(biāo)對象

#root.target

targetClass

root object

當(dāng)前被調(diào)用的目標(biāo)對象類

root.targetClass

args

root object

當(dāng)前被調(diào)用的方法的參數(shù)列表

#root.args[0]

caches

root object

當(dāng)前方法調(diào)用使用的緩存列表(如 cacheNames={"cache1","cache2"} 則有兩個 cache)

#root.caches[0].name

argument name

evaluation context

方法參數(shù)的名字,可直接用 #參數(shù)名或 #p0/#a0(0 代表參數(shù)索引)

#iban, #a0, #p0

result

evaluation context

方法執(zhí)行后的返回值(僅當(dāng)方法執(zhí)行之后判斷有效,如 cacheable 的 unless、cachePut 的表達(dá)式、cacheEvict 的beforeInvocation=false)

#result

實戰(zhàn),進(jìn)行兩次查詢方法,只出現(xiàn)一次sql語句   

@GetMapping("/{id}")
    @Cacheable(cacheNames = "emp",key = "#id",condition = "#id>0",unless = "#result == null ")
    public Employee getEmpById(@PathVariable("id") Integer id) {
        return employeeService.getEmpById(id);
    }

@Cacheable源碼分析

  1. 在運行方法之間會先去查詢Cache(緩存組件),按照cacheNames指定的名字獲取(CacheManager)先獲取相應(yīng)的緩存,第一次獲取緩存如果沒有Cache組件會自動創(chuàng)建;
  2. 去Cache里面查找緩存的內(nèi)容,使用的key默認(rèn)就是方法的參數(shù);key默認(rèn)是使用keyGenerator生成的,默認(rèn)使用SimpleKeyGenerator;
  3. 沒有查詢到緩存就調(diào)用目標(biāo)方法;
  4. 將目標(biāo)方法返回的結(jié)果放到緩存里面。

@CachePut @CacheEnvict @CacheConfig

@CachePut

調(diào)用方法,有更新緩存數(shù)據(jù),一般用于更新操作,在更新緩存時一定要和想要更新的緩存有相同的緩存名稱和相同的key(可類比同一張表的同一條數(shù)據(jù))。

@PutMapping("/update")
    @CachePut(cacheNames = "emp",key = "#employee.id")
    public void updateEmp(@RequestBody Employee employee) {
        employeeService.updateEmployee(employee);
    }

@CacheEnvict

緩存清除,清除緩存時要指定緩存的名字和key,相當(dāng)于告訴數(shù)據(jù)庫要刪除哪個表中的哪個數(shù)據(jù),key默認(rèn)為參數(shù)值。

屬性:

  • value/cacheNames:緩存的名字;
  • key:緩存的鍵;
  • allEnries:是否清除指定緩存中的所有鍵值對,默認(rèn)為false,設(shè)置為true時會清除緩存中的所有鍵值對,與key屬性二選一使用;
  • beforeInvocation:在@CacheEnvict注解的方法調(diào)用之間清除指定緩存,默認(rèn)為false,即在方法調(diào)用之后清除緩存,設(shè)置為true時則會在方法調(diào)用之間清除緩存(子啊方法調(diào)用之前還是之后清除緩存的區(qū)別在于方法調(diào)用時是否會出現(xiàn)異常,若不出現(xiàn)異常,這兩種設(shè)置沒有區(qū)別,若出現(xiàn)異常,設(shè)置為在方法調(diào)用之后清除緩存則不起作用,因為方法調(diào)用失敗了)。
@Delete("/{id}")
    @CacheEvict(cacheNames = "emp",key = "#id",beforeInvocation = true)
    public void deleteEmp(@PathVariable("id") Integer id) {
        employeeService.deleteEmployee(id);
    }

@CacheConfig

作用:標(biāo)注在類上,抽取緩存相關(guān)的公共配置,可抽取的公共配置有緩存的名字,主鍵生成器等

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
    String[] cacheNames() default {};
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
}

實列:通過@CacheConfig的cacheNames屬性指定緩存的名字之后,該類中的其他緩存注解就不必再寫value或者cacheName了,會使用該名字作為value或cacheName的值,也會遵循就近原則。

@Service
@CacheConfig(cacheNames = "emp")
public class EmployeeServiceImpl implements EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;
    @Override
    @Cacheable(key = "#empId")
    public Employee getEmpById(int empId) {
        return  employeeMapper.getEmpById(empId);
    }
}

自定義RedisCacheManager

通過前面運用緩存,我們發(fā)現(xiàn)緩存亂碼了

這時我們就需要自定義RedisCacheManager將其加入到SpringIOC容器中解決這個問題

@Configuration
public class CacheConfig {
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory
                                                  redisConnectionFactory) {
// 分別創(chuàng)建String和JSON格式序列化對象,對緩存數(shù)據(jù)key和value進(jìn)行轉(zhuǎn)換
        RedisSerializer<String> strSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jacksonSeial =
                new Jackson2JsonRedisSerializer(Object.class);
// 解決查詢緩存轉(zhuǎn)換異常的問題
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);
// 定制緩存數(shù)據(jù)序列化方式及時效
        RedisCacheConfiguration config =
                RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofDays(1))
                        .serializeKeysWith(RedisSerializationContext.SerializationPair
                                .fromSerializer(strSerializer))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair
                                .fromSerializer(jacksonSeial))
                        .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager
                .builder(redisConnectionFactory).cacheDefaults(config).build();
        return cacheManager;
    }
}

到此這篇關(guān)于SpringBoot 緩存源碼分析的文章就介紹到這了,更多相關(guān)SpringBoot 緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring標(biāo)準(zhǔn)的xml文件頭實例分析

    Spring標(biāo)準(zhǔn)的xml文件頭實例分析

    這篇文章主要介紹了Spring標(biāo)準(zhǔn)的xml文件頭實例分析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • 聊聊Java中的Native修飾符

    聊聊Java中的Native修飾符

    這篇文章主要介紹了Java中的Native修飾符使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Scala 操作Redis使用連接池工具類RedisUtil

    Scala 操作Redis使用連接池工具類RedisUtil

    這篇文章主要介紹了Scala 操作Redis使用連接池工具類RedisUtil,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • 升級dubbo2.7.4.1版本平滑遷移到注冊中心nacos

    升級dubbo2.7.4.1版本平滑遷移到注冊中心nacos

    這篇文章主要為大家介紹了2.7.4.1的dubbo平滑遷移到注冊中心nacos的兩種版本升級方案,以及為什要升級,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-02-02
  • 使用Java實現(xiàn)創(chuàng)建Excel表單控件

    使用Java實現(xiàn)創(chuàng)建Excel表單控件

    在數(shù)據(jù)填報時,創(chuàng)建Excel表單控件是一項常見的任務(wù),它可以極大地簡化數(shù)據(jù)收集和處理的過程,本文主要介紹了如何使用Java實現(xiàn)創(chuàng)建Excel表單控件,感興趣的可以了解下
    2024-03-03
  • Spring?cloud?OpenFeign中動態(tài)URl、動態(tài)傳遞接口地址代碼示例

    Spring?cloud?OpenFeign中動態(tài)URl、動態(tài)傳遞接口地址代碼示例

    openFeign是作為微服務(wù)之間調(diào)用的解決方案,每個微服務(wù)項目是必不可少的,下面這篇文章主要給大家介紹了關(guān)于Spring?cloud?OpenFeign中動態(tài)URl、動態(tài)傳遞接口地址的相關(guān)資料,需要的朋友可以參考下
    2024-02-02
  • Java中Optional.of()方法及源碼解析(非常詳細(xì)!)

    Java中Optional.of()方法及源碼解析(非常詳細(xì)!)

    這篇文章主要給大家介紹了關(guān)于Java中Optional.of()方法及源碼解析的相關(guān)資料,Java中java.util .Optional類的of()方法用于獲得該Optional類中具有指定類型的指定值的一個實例,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • Java編寫緩存工具類的示例代碼

    Java編寫緩存工具類的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何基于Java編寫一個緩存工具類,文中的示例代碼講解詳細(xì),具有一定的參考價值,感興趣的小伙伴可以了解一下
    2023-07-07
  • Mybatis 逆向工程的三種方法詳解

    Mybatis 逆向工程的三種方法詳解

    這篇文章主要介紹了Mybatis 逆向工程的三種方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Spring Boot Actuator監(jiān)控的簡單使用方法示例代碼詳解

    Spring Boot Actuator監(jiān)控的簡單使用方法示例代碼詳解

    這篇文章主要介紹了Spring Boot Actuator監(jiān)控的簡單使用,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06

最新評論

丰台区| 双流县| 临武县| 化德县| 手机| 印江| 龙里县| 岳普湖县| 湛江市| 收藏| 松溪县| 乡城县| 罗平县| 阜康市| 桃源县| 南平市| 兴山县| 岳池县| 邮箱| 秦皇岛市| 天峨县| 台前县| 孟州市| 宿迁市| 建始县| 曲水县| 宁阳县| 远安县| 澎湖县| 永春县| 清丰县| 集安市| 哈尔滨市| 岳普湖县| 威宁| 纳雍县| 集安市| 长春市| 山阳县| 呼图壁县| 嘉善县|