日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

SpringCache通用緩存學習

作者:編程指南針 更新時間: 2022-06-08 編程語言

?

1.1 介紹

Spring Cache是一個框架,實現了基于注解的緩存功能,只需要簡單地加一個注解,就能實現緩存功能,大大簡化我們在業務中操作緩存的代碼。

Spring Cache只是提供了一層抽象,底層可以切換不同的cache實現。具體就是通過CacheManager接口來統一不同的緩存技術。CacheManager是Spring提供的各種緩存技術抽象接口。

針對不同的緩存技術需要實現不同的CacheManager:

CacheManager 描述
EhCacheCacheManager 使用EhCache作為緩存技術
GuavaCacheManager 使用Google的GuavaCache作為緩存技術
RedisCacheManager 使用Redis作為緩存技術

1.2 注解

在SpringCache中提供了很多緩存操作的注解,常見的是以下的幾個:

注解 說明
@EnableCaching 開啟緩存注解功能
@Cacheable 在方法執行前spring先查看緩存中是否有數據,如果有數據,則直接返回緩存數據;若沒有數據,調用方法并將方法返回值放到緩存中
@CachePut 將方法的返回值放到緩存中
@CacheEvict 將一條或多條數據從緩存中刪除

在spring boot項目中,使用緩存技術只需在項目中導入相關緩存技術的依賴包,并在啟動類上使用@EnableCaching開啟緩存支持即可。

例如,使用Redis作為緩存技術,只需要導入Spring data Redis的maven坐標即可。

1.3 入門程序

接下來,我們將通過一個入門案例來演示一下SpringCache的常見用法。上面我們提到,SpringCache可以集成不同的緩存技術,如Redis、Ehcache甚至我們可以使用Map來緩存數據, 接下來我們在演示的時候,就先通過一個Map來緩存數據,最后我們再換成Redis來緩存。

1.3.1 環境準備

1). 數據庫準備

將今天資料中的SQL腳本直接導入數據庫中。

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2). 導入基礎工程

基礎環境的代碼,在我們今天的資料中已經準備好了, 大家只需要將這個工程導入進來就可以了。

package com.znz.entity;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String name;

    private int age;

    private String address;

}

package com.znz.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.entity.User;
import com.itheima.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserService userService;

    /**
     * CachePut:將方法返回值放入緩存
     * value:緩存的名稱,每個緩存名稱下面可以有多個key
     * key:緩存的key
     */
    @CachePut(value = "userCache",key = "#user.id")
    @PostMapping
    public User save(User user){
        userService.save(user);
        return user;
    }

    /**
     * CacheEvict:清理指定緩存
     * value:緩存的名稱,每個緩存名稱下面可以有多個key
     * key:緩存的key
     */
    @CacheEvict(value = "userCache",key = "#p0")
    //@CacheEvict(value = "userCache",key = "#root.args[0]")
    //@CacheEvict(value = "userCache",key = "#id")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }

    //@CacheEvict(value = "userCache",key = "#p0.id")
    //@CacheEvict(value = "userCache",key = "#user.id")
    //@CacheEvict(value = "userCache",key = "#root.args[0].id")
    @CacheEvict(value = "userCache",key = "#result.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

    /**
     * Cacheable:在方法執行前spring先查看緩存中是否有數據,如果有數據,則直接返回緩存數據;若沒有數據,調用方法并將方法返回值放到緩存中
     * value:緩存的名稱,每個緩存名稱下面可以有多個key
     * key:緩存的key
     * condition:條件,滿足條件時才緩存數據
     * unless:滿足條件則不緩存
     */
    @Cacheable(value = "userCache",key = "#id",unless = "#result == null")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

    @Cacheable(value = "userCache",key = "#user.id + '_' + #user.name")
    @GetMapping("/list")
    public List<User> list(User user){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getId() != null,User::getId,user.getId());
        queryWrapper.eq(user.getName() != null,User::getName,user.getName());
        List<User> list = userService.list(queryWrapper);
        return list;
    }
}

package com.znz.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User>{
}

package?com.znz.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.entity.User;

public interface UserService extends IService<User> {
}

package com.znz.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.entity.User;
import com.itheima.mapper.UserMapper;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}

由于SpringCache的基本功能是Spring核心(spring-context)中提供的,所以目前我們進行簡單的SpringCache測試,是可以不用額外引入其他依賴的。

3). 注入CacheManager

我們可以在UserController注入一個CacheManager,在Debug時,我們可以通過CacheManager跟蹤緩存中數據的變化。

而在上述的這幾個實現中,默認使用的是 ConcurrentMapCacheManager。稍后我們可以通過斷點的形式跟蹤緩存數據的變化。

4). 引導類上加@EnableCaching

在引導類上加該注解,就代表當前項目開啟緩存注解功能。

import lombok.extern.slf4j.Slf4j;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;
@Slf4j@SpringBootApplication@EnableCachingpublic class CacheDemoApplication {    public static void main(String[] args) {        SpringApplication.run(CacheDemoApplication.class,args);        log.info("項目啟動成功...");    }}

1.3.2 @CachePut注解

@CachePut 說明:

作用: 將方法返回值,放入緩存

value: 緩存的名稱, 每個緩存名稱下面可以有很多key

key: 緩存的key ?----------> 支持Spring的表達式語言SPEL語法

1). 在save方法上加注解@CachePut

當前UserController的save方法是用來保存用戶信息的,我們希望在該用戶信息保存到數據庫的同時,也往緩存中緩存一份數據,我們可以在save方法上加上注解 @CachePut,用法如下:

?/**
?* CachePut:將方法返回值放入緩存
?* value:緩存的名稱,每個緩存名稱下面可以有多個key
?* key:緩存的key
?*/
?@CachePut(value?=?"userCache",?key?=?"#user.id")
?@PostMapping
?public?User?save(User?user){
??? ?userService.save(user);
??? ?return?user;
?}

key的寫法如下:

#user.id : #user指的是方法形參的名稱, id指的是user的id屬性 , 也就是使用user的id屬性作為key ;

#user.name: #user指的是方法形參的名稱, name指的是user的name屬性 ,也就是使用user的name屬性作為key ;

#result.id : #result代表方法返回值,該表達式 代表以返回對象的id屬性作為key ;

#result.name : #result代表方法返回值,該表達式 代表以返回對象的name屬性作為key ;

2). 測試

啟動服務,通過postman請求訪問UserController的方法, 然后通過斷點的形式跟蹤緩存數據。

第一次訪問時,緩存中的數據是空的,因為save方法執行完畢后才會緩存數據。

第二次訪問時,我們通過debug可以看到已經有一條數據了,就是上次保存的數據,已經緩存了,緩存的key就是用戶的id。

注意: 上述的演示,最終的數據,實際上是緩存在ConcurrentHashMap中,那么當我們的服務器重啟之后,緩存中的數據就會丟失。我們后面使用了Redis來緩存就不存在這樣的問題了。

1.3.3 @CacheEvict注解

@CacheEvict 說明:

作用: 清理指定緩存

value: 緩存的名稱,每個緩存名稱下面可以有多個key

key: 緩存的key ?----------> 支持Spring的表達式語言SPEL語法

1). 在 delete 方法上加注解@CacheEvict

當我們在刪除數據庫user表的數據的時候,我們需要刪除緩存中對應的數據,此時就可以使用@CacheEvict注解, 具體的使用方式如下:

?/**
?* CacheEvict:清理指定緩存
?* value:緩存的名稱,每個緩存名稱下面可以有多個key
?* key:緩存的key
?*/
?@CacheEvict(value?=?"userCache",key?=?"#p0") ?//#p0 代表第一個參數
?//@CacheEvict(value = "userCache",key = "#root.args[0]") //#root.args[0] 代表第一個參數
?//@CacheEvict(value = "userCache",key = "#id") //#id 代表變量名為id的參數
?@DeleteMapping("/{id}")
?public?void?delete(@PathVariable?Long?id){
??? ?userService.removeById(id);
?}

2). 測試

要測試緩存的刪除,我們先訪問save方法4次,保存4條數據到數據庫的同時,也保存到緩存中,最終我們可以通過debug看到緩存中的數據信息。然后我們在通過postman訪問delete方法, 如下:

刪除數據時,通過debug我們可以看到已經緩存的4條數據:

當執行完delete操作之后,我們再次保存一條數據,在保存的時候debug查看一下刪除的ID值是否已經被刪除。

3). 在 update 方法上加注解@CacheEvict

在更新數據之后,數據庫的數據已經發生了變更,我們需要將緩存中對應的數據刪除掉,避免出現數據庫數據與緩存數據不一致的情況。

?
//@CacheEvict(value = "userCache",key = "#p0.id") ? //第一個參數的id屬性
?//@CacheEvict(value = "userCache",key = "#user.id") //參數名為user參數的id屬性
?//@CacheEvict(value = "userCache",key = "#root.args[0].id") //第一個參數的id屬性
?@CacheEvict(value?=?"userCache",key?=?"#result.id") ? ? ? ??//返回值的id屬性
?@PutMapping
?public?User?update(User?user){
??? ?userService.updateById(user);
??? ?return?user;
?}

加上注解之后,我們可以重啟服務,然后測試方式,基本和上述相同,先緩存數據,然后再更新某一條數據,通過debug的形式查詢緩存數據的情況。

1.3.4 @Cacheable注解

@Cacheable 說明:

作用: 在方法執行前,spring先查看緩存中是否有數據,如果有數據,則直接返回緩存數據;若沒有數據,調用方法并將方法返回值放到緩存中

value: 緩存的名稱,每個緩存名稱下面可以有多個key

key: 緩存的key ?----------> 支持Spring的表達式語言SPEL語法

1). 在getById上加注解@Cacheable

?/**
?* Cacheable:在方法執行前spring先查看緩存中是否有數據,如果有數據,則直接返回緩存數據;若沒有數據,調用方法并將方法返回值放到緩存中
?* value:緩存的名稱,每個緩存名稱下面可以有多個key
?* key:緩存的key
?*/
?@Cacheable(value?=?"userCache",key?=?"#id")
?@GetMapping("/{id}")
?public?User?getById(@PathVariable?Long?id){
??? ?User?user?=?userService.getById(id);
??? ?return?user;
?}

2). 測試

我們可以重啟服務,然后通過debug斷點跟蹤程序執行。我們發現,第一次訪問,會請求我們controller的方法,查詢數據庫。后面再查詢相同的id,就直接獲取到數據庫,不用再查詢數據庫了,就說明緩存生效了。

當我們在測試時,查詢一個數據庫不存在的id值,第一次查詢緩存中沒有,也會查詢數據庫。而第二次再查詢時,會發現,不再查詢數據庫了,而是直接返回,那也就是說如果根據ID沒有查詢到數據,那么會自動緩存一個null值。我們可以通過debug,驗證一下:

我們能不能做到,當查詢到的值不為null時,再進行緩存,如果為null,則不緩存呢? 答案是可以的。

3). 緩存非null值

在@Cacheable注解中,提供了兩個屬性分別為:condition, unless 。

condition : 表示滿足什么條件, 再進行緩存 ;

unless : 表示滿足條件則不緩存 ; 與上述的condition是反向的 ;

具體實現方式如下:

?/**
??* Cacheable:在方法執行前spring先查看緩存中是否有數據,如果有數據,則直接返回緩存數據;若沒有數據,調用方法并將方法返回值放到緩存中
??* value:緩存的名稱,每個緩存名稱下面可以有多個key
??* key:緩存的key
??* condition:條件,滿足條件時才緩存數據
??* unless:滿足條件則不緩存
??*/
?@Cacheable(value?=?"userCache",key?=?"#id",?unless?=?"#result == null")
?@GetMapping("/{id}")
?public?User?getById(@PathVariable?Long?id){
??? ?User?user?=?userService.getById(id);
??? ?return?user;
?}

注意:此處,我們使用的時候只能夠使用 unless, 因為在condition中,我們是無法獲取到結果 #result的。

4). 在list方法上加注解@Cacheable

在list方法中進行查詢時,有兩個查詢條件,如果傳遞了id,根據id查詢;如果傳遞了name, 根據name查詢,那么我們緩存的key在設計的時候,就需要既包含id,又包含name。具體的代碼實現如下:

?@Cacheable(value?=?"userCache",key?=?"#user.id + '_' + #user.name")
?@GetMapping("/list")
?public?List<User>?list(User?user){
??? ?LambdaQueryWrapper<User>?queryWrapper?=?new?LambdaQueryWrapper<>();
??? ?queryWrapper.eq(user.getId()?!=?null,User::getId,user.getId());
??? ?queryWrapper.eq(user.getName()?!=?null,User::getName,user.getName());
??? ?List<User>?list?=?userService.list(queryWrapper);
??? ?return?list;
?}

然后再次重啟服務,進行測試。

第一次查詢時,需要查詢數據庫,在后續的查詢中,就直接查詢了緩存,不再查詢數據庫了。

?

2 ?集成Redis

在使用上述默認的ConcurrentHashMap做緩存時,服務重啟之后,之前緩存的數據就全部丟失了,操作起來并不友好。在項目中使用,我們會選擇使用redis來做緩存,主要需要操作以下幾步:

1). pom.xml

?<dependency>
??? ?<groupId>org.springframework.boot</groupId>
??? ?<artifactId>spring-boot-starter-cache</artifactId>
?</dependency>
?
?<dependency>
??? ?<groupId>org.springframework.boot</groupId>
??? ?<artifactId>spring-boot-starter-data-redis</artifactId>
?</dependency>

2). application.yml

?spring:
??redis:
??? host:?192.168.200.200
??? port:?6379
??? password:?root@123456
??? database:?0
??cache:
??? redis:
??? ? time-to-live:?1800000 ??#設置緩存過期時間,可選

3). 測試

重新啟動項目,通過postman發送根據id查詢數據的請求,然后通過redis的圖形化界面工具,查看redis中是否可以正常的緩存數據。

以上是針對SpringCache通用緩存框架的應用介紹,有了它以后,可以理方便的在我們的程序中使用緩存,而且可以無縫移植。

?

原文鏈接:https://blog.csdn.net/whirlwind526/article/details/124929476

欄目分類
最近更新