springBoot緩存源碼分析

一、springBoot與緩存

1. JSR107

Java Caching定義了5個核心接口,分別是CachingProvider, CacheManager,Cache, EntryExpiry

  • CachingProvider定義了創建、配置、獲取、管理和控制多個CacheManager,一個應用可以在運行期訪問多個CachingProvider
  • CacheManager定義了創建、配置、獲取、管理和控制多個唯一命名的Cache,這些Cache存在於CacheManager的上下文中,一個CacheManager僅被一個CachingProvider所擁有
  • Cache是一個類似Map的數據結構並臨時存儲以Key爲索引的值,一個Cache僅被一個CacheManager所擁有,在後面的源碼分析中我們可以發現Cache其實就是一個HashMap爲主的一個類
  • Entry是一個存儲在Cache中的key-value這裏的Entry和Map中的內部類是一個意思,所以在這裏也更能確定Cache就是一個以Map爲主要數據結構的類在這裏插入圖片描述
  • Expiry 每一個存儲在Cache中的條目有一個定義的有效期。一旦超過這個時間,條目爲過期的狀態。一旦過期,條目將不可訪問、更新和刪除。緩存有效期可以通過ExpiryPolicy設置。

可以用這樣一幅圖總結一下:
在這裏插入圖片描述

2.spring的緩存抽象

在這裏插入圖片描述
spring提供了一系列的註解來方便我們的開發,今天我們主要是進行源代碼的分析,我們以@Cacheable註解爲例;

二、開始前的準備

緩存其實就是在應用程序和數據庫之間加的一箇中間層,用來加速數據的檢索;在這裏插入圖片描述
所以我們今天的示例要先在數據庫中創建一個User表:

1.創建User表

在這裏插入圖片描述

2.創建一個springBoot應用

idea上創建springBoot是很方便的這裏就不演示了;
在這裏插入圖片描述
這些是我導入的依賴,Edit Starters是一個idea插件可以讓你的springBoot項目在創建以後以勾選的方式添加依賴;

3.配置springBoot項目

主要就是一個開啓緩存的註解
在這裏插入圖片描述
在這裏插入圖片描述
這裏最後的debug=true開啓以後就可以看到sprngBoot程序啓動的時候自動配置的信息;
接下來就是代碼:
首先要有一個User實體類:

@Data
public class User {
    private int id;
    private String name;
}

這裏我使用了一個idealombok插件,可以讓你不用再去寫那些get set方法了;

Mapper:這裏我們爲了方便採用註解開發(但是我本人還是喜歡xml方式去寫sql)

@Mapper
public interface UserMapper {

    @Select("select *from user where id = #{id}")
    User getUserById(int id);

    @Update("update user set name = #{name} where id = #{id}")
    void updateUser(User user);

    @Delete("delete from user where id = #{id}")
    void deleteUser(int id);

    @Insert("insert into user values(#{id}, #{name})")
    void insertUser(User user);
}

這裏我們就有了一個基本的增刪改查的接口;

Service層:這裏應該是一個接口對應一個實現,爲了方便我們就不寫接口了

@Service
public class UserService {

    @Autowired
    UserMapper userMapper;

    @Cacheable(cacheNames = "user", key = "#root.methodName+'['+#id+']'")
    public User getUser(int id) {
        System.out.println("查詢");
        User user = userMapper.getUserById(id);
        return user;
    }
}

應爲我們是一個web項目,所以我們還需要一個Controller:

@RestController
public class UserController {

    @Autowired
    UserService userService;

    @GetMapping("/getUser/{id}")
    public User getUser(@PathVariable("id") int id) {
        return userService.getUser(id);
    }
}

4.演示

我們啓動項目:
在這裏插入圖片描述
第一次訪問後臺並沒有緩存所以我們訪問了數據庫有sql語句
在這裏插入圖片描述
當我們第二次訪問這個url我們會發現我們並沒有去訪問數據庫,甚至沒有去調用UserService中的getUser方法
在這裏插入圖片描述

三、原理分析

在springBoot中所有的自動配置都是...AutoConfiguration所以我們去搜CacheAutoConfiguration這個類
在這個類中有一個靜態內部類CacheConfigurationImportSelector他有一個selectImport方法是用來給容器中添加一些緩存要用的組件;
在這裏插入圖片描述
我們在這裏打上斷點,debug調試一下看看imports中有哪些緩存組件
在這裏插入圖片描述
我們可以看到這裏總共有十個緩存組件;我們隨便去看一個
會發現在他的註解上表明瞭什麼時候使用這個組件;
在這裏插入圖片描述
那麼接下來我們來看看springBoot默認使用的緩存組件是什麼;
(這裏就不把所有的查詢結果放出了)
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
我們最終會發現只有SimpleCacheConfiguration是被使用的,所以也就說明默認情況下使用SimpleCacheConfiguration;
然後我們進入到SimpleCacheConfiguration中:
在這裏插入圖片描述
我們會發現他給springBoot容器添加了一個bean,是一個CacheManager;
ConcurrentMapCacheManager實現了CacheManager接口
在這裏插入圖片描述
這裏要說一個@Nullable註解這個註解是說傳入的參數可以爲null

在寫程序的時候你可以定義是否可爲空指針。通過使用像@NotNull和@Nullable之類的annotation來聲明一個方法是否是空指針安全的。現代的編譯器、IDE或者工具可以讀此annotation並幫你添加忘記的空指針檢查,或者向你提示出不必要的亂七八糟的空指針檢查。IntelliJ和findbugs已經支持了這些annotation。這些annotation同樣是JSR 305的一部分,但即便IDE或工具中沒有,這個annotation本身可以作爲文檔。看到@NotNull和@Nullable,程序員自己可以決定是否做空指針檢查。順便說一句,這個技巧對Java程序員來說相對比較新,要採用需要一段時間。

在這裏插入圖片描述
getCache方法使用了雙重鎖校驗(這種驗證機制一般是用在單例模式中)
我們可以看到如果沒有Cache會調用
cache = this.createConcurrentMapCache(name);
在這裏插入圖片描述
這個方法會創建一個ConcurrentMapCache這個就是我們說的Cache;
在這裏插入圖片描述
在這個類裏面有這樣三個屬性;

 private final ConcurrentMap<Object, Object> store;

這個就是前文中的Entry用來存放鍵值對;
ConcurrentMapCache中我們會看到一些操作Cache的方法我選幾個重要的
在這裏插入圖片描述
在這裏插入圖片描述
lookup方法是根據key來找value的;
put方法顧名思義是用來添加鍵值對的;
到這裏基本上就結束了,接下來我們來詳細分析一下@Cacheable註解

四、@Cacheable分析

我們在上述的兩個方法上打上斷點;debug運行springBoot;
訪問getUser接口;
在這裏插入圖片描述
我們會發現他來到了lookup方法這裏,說明註解的執行在被註解的方法前,然後這裏我們會返回null;
我們放行到下一個註解會發現;調用了put方法
在這裏插入圖片描述
添加了Cache;然後我們第二次對getUser接口發起請求我們會發現打斷點的兩個方法沒有被執行在這裏插入圖片描述
因爲在這裏cache不爲null了,直接被返回了;


以上就是我對於springBoot緩存的理解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章