Spring緩存支持

------------------------------------------------------------------------------

開門見代碼:

[git地址走起](https://github.com/lamymay/ray.git)


內(nèi)存的速度遠(yuǎn)遠(yuǎn)大于硬盤的速度酸钦,當(dāng)我們需要重復(fù)獲取相同的數(shù)據(jù)的時候,一次又一次的請求數(shù)據(jù)庫或遠(yuǎn)程服務(wù),導(dǎo)致大量時間都消耗在數(shù)據(jù)庫查詢或遠(yuǎn)程方法調(diào)用上面,性能下降,這時候就需要使用到緩存技術(shù)了腻扇。

本文介紹SpringBoot 如何使用redis做緩存,如何對redis緩存進(jìn)行定制化配置(如key的有效期)以及初始化redis做緩存。

使用具體的代碼介紹相關(guān)注解及其屬性的用法滋恬。

@Cacheable,

@CacheEvict抱究,

@CachePut恢氯,

@CacheConfig


子項(xiàng)目 cache 即是 Spring緩存的演示項(xiàng)目,相關(guān)sql在cache子項(xiàng)目的根目錄,

Spring定義了?org.springframework.cache.CacheManager?和?org.springframework.cache.Cache?接口來統(tǒng)一不同緩存技術(shù)勋拟。其中CacheManager是Spring提供的各種緩存技術(shù)抽象接口勋磕,內(nèi)部使用Cache接口進(jìn)行緩存的增刪改查操作,我們一般不會直接和Cache打交道敢靡。

針對不同的緩存技術(shù)挂滓,Spring有不同的CacheManager實(shí)現(xiàn)類,定義如下表:

CacheManager描述

SimpleCacheManager使用簡單的Collection存儲緩存數(shù)據(jù)啸胧,用來做測試用

ConcurrentMapCacheManager使用ConcurrentMap存儲緩存數(shù)據(jù)

EhCacheCacheManager使用EhCache作為緩存技術(shù)

GuavaCacheManager使用Google Guava的GuavaCache作為緩存技術(shù)

JCacheCacheManager使用JCache(JSR-107)標(biāo)準(zhǔn)的實(shí)現(xiàn)作為緩存技術(shù)赶站,比如Apache Commons JCS

RedisCacheManager使用Redis作為緩存技術(shù)

----------------------------------------------------------------------------

1. 在我們使用任意一個實(shí)現(xiàn)的CacheManager的時候,需要注冊實(shí)現(xiàn)Bean:

/**

* EhCache的配置

*/

@Bean

public EhCacheCacheManagercacheManager(CacheManager cacheManager) {

? ? return new EhCacheCacheManager(cacheManager);

}

當(dāng)然纺念,各種緩存技術(shù)都有很多其他配置贝椿,但是配置cacheManager是必不可少的。

聲明式緩存注解

Spring提供4個注解來聲明緩存規(guī)則陷谱,如下表所示:

注解? ?說明

@Cacheable方法執(zhí)行前先看緩存中是否有數(shù)據(jù)烙博,如果有直接返回。如果沒有就調(diào)用方法烟逊,并將方法返回值放入緩存

@CachePut無論怎樣都會執(zhí)行方法渣窜,并將方法返回值放入緩存

@CacheEvict將數(shù)據(jù)從緩存中刪除

@Caching可通過此注解組合多個注解策略在一個方法上面

@Cacheable 、@CachePut 宪躯、@CacheEvict都有value屬性乔宿,指定要使用的緩存名稱,而key屬性指定緩存中存儲的鍵眷唉。

2.?集成Redis緩存

接下來將講解如何集成redis來實(shí)現(xiàn)緩存予颤。

2.1?安裝redis

安裝和配置redis服務(wù)器網(wǎng)上很多教程,這里就不多講了冬阳。在linux服務(wù)器上面安裝一個redis蛤虐,啟動后端口號為默認(rèn)的6379。

2.2?添加maven依賴

? ? org.springframework.boot

? ? spring-boot-starter-data-redis

2.3?配置application.yml

指定緩存的類型

配置redis的服務(wù)器信息

spring:

? profiles: dev

cache:

type: REDIS

? redis:

? ? host: 123.207.66.156

? ? port: 6379

timeout: 0

database: 0

? ? pool:

max-active: 100

max-wait: -1

max-idle: 8

min-idle: 0

3.?緩存配置類

重新配置?RedisCacheManager?肝陪,使用新的自定義配置值:

@Configuration

@EnableCaching

public class RedisCacheConfig {

? ? /**

? ? * 重新配置RedisCacheManager

? ? */

? ? @Autowired

? ? public voidconfigRedisCacheManger(RedisCacheManager rd) {

? ? ? ? rd.setDefaultExpiration(100L);

? ? }

}

keyGenerator

一般來講我們使用key屬性就可以滿足大部分要求驳庭,但是如果你還想更好的自定義key,可以實(shí)現(xiàn)keyGenerator氯窍。

這個屬性為定義key生成的類饲常,和key屬性不能同時存在。

在?RedisCacheConfig?配置類中添加我自定義的KeyGenerator:

/**

* 自定義緩存key的生成類實(shí)現(xiàn)

*/

@Bean(name = "myKeyGenerator")

public KeyGeneratormyKeyGenerator() {

? ? return new KeyGenerator() {

? ? ? ? @Override

? ? ? ? public Objectgenerate(Object o, Method method, Object... params) {

? ? ? ? ? ? logger.info("自定義緩存狼讨,使用第一參數(shù)作為緩存key贝淤,params = " + Arrays.toString(params));

? ? ? ? ? ? // 僅僅用于測試,實(shí)際不可能這么寫

? ? ? ? ? ? return params[0];

? ? ? ? }

? ? };

}

經(jīng)過以上配置后政供,redis緩存管理對象已經(jīng)生成播聪。下面簡單介紹如何使用朽基。

4.?使用

在service中定義增刪改的幾個常見方法,通過注解實(shí)現(xiàn)緩存:

@Service

@CacheConfig(cacheNames="users")

public class UserService {

? ? private Logger logger = LoggerFactory.getLogger(this.getClass());

? ? @Resource

? ? private UserMapper userMapper;

? ? /**

? ? * cacheNames 設(shè)置緩存的值

? ? * key:指定緩存的key离陶,這是指參數(shù)id值稼虎。 key可以使用spEl表達(dá)式

*@paramid

*@return

? ? */

? ? @Cacheable(cacheNames="user1", key="#id")

? ? public UsergetById(int id) {

? ? ? ? logger.info("獲取用戶start...");

? ? ? ? return userMapper.selectById(id);

? ? }

? ? /***

? ? * 如果設(shè)置sync=true,

? ? * 如果緩存中沒有數(shù)據(jù)招刨,多個線程同時訪問這個方法霎俩,則只有一個方法會執(zhí)行到方法,其它方法需要等待

? ? * 如果緩存中已經(jīng)有數(shù)據(jù)沉眶,則多個線程可以同時從緩存中獲取數(shù)據(jù)

*@paramid

*@return

? ? */

? ? @Cacheable(cacheNames="user1", key="#id", sync = true)

? ? public UsergetById2(int id) {

? ? ? ? logger.info("獲取用戶start...");

? ? ? ? return userMapper.selectById(id);

? ? }


? ? /**

? ? * 以上我們使用默認(rèn)的keyGenerator打却,對應(yīng)spring的SimpleKeyGenerator

? ? * 如果你的使用很復(fù)雜,我們也可以自定義myKeyGenerator的生成key

? ? * <p>

? ? * key和keyGenerator是互斥沦寂,如果同時制定會出異常

? ? * The key and keyGenerator parameters are mutually exclusive and an operation specifying both will result in an exception.

? ? *

*@paramid

*@return

? ? */

? ? @Cacheable(cacheNames = "user1", keyGenerator = "myKeyGenerator")

? ? public UserqueryUserById(int id) {

? ? ? ? logger.info("queryUserById,id={}", id);

? ? ? ? return userMapper.selectById(id);

? ? }

? ? /**

? ? * 每次執(zhí)行都會執(zhí)行方法学密,同時使用新的返回值的替換緩存中的值

*@paramuser

? ? */

? ? @CachePut(cacheNames="user1", key="#user.id")

? ? public voidcreateUser(User user) {

? ? ? ? logger.info("創(chuàng)建用戶start...");

? ? ? ? userMapper.insert(user);

? ? }

? ? /**

? ? * 每次執(zhí)行都會執(zhí)行方法,同時使用新的返回值的替換緩存中的值

*@paramuser

? ? */

? ? @CachePut(cacheNames="user1", key="#user.id")

? ? public voidupdateUser(User user) {

? ? ? ? logger.info("更新用戶start...");

? ? ? ? userMapper.updateById(user);

? ? }

? ? /**

? ? * 對符合key條件的記錄從緩存中user1移除

? ? */

? ? @CacheEvict(cacheNames="user1", key="#id")

? ? public voiddeleteById(int id) {

? ? ? ? logger.info("刪除用戶start...");

? ? ? ? userMapper.deleteById(id);

? ? }

? ? /**

? ? * allEntries = true: 清空user1里的所有緩存

? ? */

? ? @CacheEvict(cacheNames="user1", allEntries=true)

? ? public voidclearUser1All(){

? ? ? ? logger.info("clearAll");

? ? }

}

注意可以在類上面通過?@CacheConfig?配置全局緩存名稱传藏,方法上面如果也配置了就會覆蓋。

然后寫個測試類:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = Application.class)

public class UserServiceTest {

? ? @Autowired

? ? private UserService userService;

? ? @Test

? ? public voidtestCache() {

? ? ? ? int id = new Random().nextInt(100);

? ? ? ? User user = new User(id, "admin", "admin");

? ? ? ? userService.createUser(user);

? ? ? ? User user1 = userService.getById(id); // 第1次訪問

? ? ? ? assertEquals(user1.getPassword(), "admin");

? ? ? ? User user2 = userService.getById(id); // 第2次訪問

? ? ? ? assertEquals(user2.getPassword(), "admin");

? ? ? ? User user3 = userService.queryUserById(id); // 第3次訪問彤守,使用自定義的KeyGenerator

? ? ? ? assertEquals(user3.getPassword(), "admin");

? ? ? ? user.setPassword("123456");

? ? ? ? userService.updateUser(user);

? ? ? ? User user4 = userService.getById(id); // 第4次訪問

? ? ? ? assertEquals(user4.getPassword(), "123456");

? ? ? ? userService.deleteById(id);

? ? ? ? assertNull(userService.getById(id));

? ? }

}

下面是測試的打印日志一部分:

Started UserServiceTest in 12.919 seconds (JVM runni

創(chuàng)建用戶start...

==>? Preparing:INSERTINTOt_user(id, username, `

==> Parameters: 14(Integer), admin(String), admin(St

<==? ? Updates: 1

獲取用戶start...

==>? Preparing: SELECT id AS id,username,`password`

==> Parameters: 14(Integer)

<==? ? ? Total: 1

自定義緩存毯侦,使用第一參數(shù)作為緩存key,params = [14]

更新用戶start...

==>? Preparing: UPDATE t_user SET username=?, `passw

==> Parameters:admin(String), 123456(String), 14(In

<==? ? Updates: 1

獲取用戶start...

==>? Preparing:SELECTidASid,username,`password`

==> Parameters: 14(Integer)

<==? ? ? Total: 1

刪除用戶start...

==>? Preparing:DELETEFROMt_userWHEREid=?

==> Parameters: 14(Integer)

<==? ? Updates: 1

獲取用戶start...

==>? Preparing:SELECTidASid,username,`password`

==> Parameters: 14(Integer)

<==? ? ? Total: 0

可以看到具垫,第2次侈离、第3次獲取的時候并沒有執(zhí)行方法,說明緩存生效了筝蚕。后面更新會同時更新緩存卦碾,取出來的也是更新后的數(shù)據(jù)。

切換緩存技術(shù)

得益于SpringBoot的自動配置機(jī)制起宽,切換緩存技術(shù)除了替換相關(guān)maven依賴包和配置Bean外洲胖,使用方式和實(shí)例中一樣,不需要修改業(yè)務(wù)代碼坯沪。如果你要切換到其他緩存技術(shù)非常簡單绿映。

EhCache

當(dāng)我們需要使用EhCache作為緩存技術(shù)的時候,只需要在pom.xml中添加EhCache的依賴:

? ? net.sf.ehcache

? ? ehcahe

EhCache的配置文件ehcache.xml只需要放到類路徑下面腐晾,SpringBoot會自動掃描叉弦,例如:

<?xml version="1.0" encoding="UTF-8"?>

? ? ? ? xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"

? ? ? ? updateCheck="false" monitoring="autodetect"

? ? ? ? dynamicConfig="true">



? ? ? ? ? ? maxElementsInMemory="50000"

? ? ? ? ? ? eternal="false"

? ? ? ? ? ? timeToIdleSeconds="3600"

? ? ? ? ? ? timeToLiveSeconds="3600"

? ? ? ? ? ? overflowToDisk="true"

? ? ? ? ? ? diskPersistent="false"

? ? ? ? ? ? diskExpiryThreadIntervalSeconds="120"

? ? />


? ? ? ? ? maxEntriesLocalHeap="2000"

? ? ? ? ? eternal="false"

? ? ? ? ? timeToIdleSeconds="3600"

? ? ? ? ? timeToLiveSeconds="3600"

? ? ? ? ? overflowToDisk="false"

? ? ? ? ? statistics="true">


SpringBoot會為我們自動配置?EhCacheCacheManager?這個Bean,不過你也可以自己定義藻糖。

Guava

當(dāng)我們需要Guava作為緩存技術(shù)的時候淹冰,只需要在pom.xml中增加Guava的依賴即可:

? ? com.google.guava

? ? guava

? ? 18.0

SpringBoot會為我們自動配置?GuavaCacheManager?這個Bean。

Redis

最后還提一點(diǎn)巨柒,本篇采用Redis作為緩存技術(shù)樱拴,添加了依賴:

? ? org.springframework.boot

? ? spring-boot-starter-data-redis

SpringBoot會為我們自動配置?RedisCacheManager?這個Bean柠衍,同時還會配置?RedisTemplate?這個Bean。后面這個Bean就是下一篇要講解的操作Redis數(shù)據(jù)庫用疹鳄,這個就比單純注解緩存強(qiáng)大和靈活的多了拧略。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瘪弓,隨后出現(xiàn)的幾起案子垫蛆,更是在濱河造成了極大的恐慌,老刑警劉巖腺怯,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袱饭,死亡現(xiàn)場離奇詭異,居然都是意外死亡呛占,警方通過查閱死者的電腦和手機(jī)虑乖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晾虑,“玉大人疹味,你說我怎么就攤上這事≈钠” “怎么了糙捺?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笙隙。 經(jīng)常有香客問我洪灯,道長,這世上最難降的妖魔是什么竟痰? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任签钩,我火速辦了婚禮,結(jié)果婚禮上坏快,老公的妹妹穿的比我還像新娘铅檩。我一直安慰自己,他們只是感情好假消,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布柠并。 她就那樣靜靜地躺著,像睡著了一般富拗。 火紅的嫁衣襯著肌膚如雪臼予。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天啃沪,我揣著相機(jī)與錄音粘拾,去河邊找鬼。 笑死创千,一個胖子當(dāng)著我的面吹牛缰雇,可吹牛的內(nèi)容都是我干的入偷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼械哟,長吁一口氣:“原來是場噩夢啊……” “哼疏之!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起暇咆,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锋爪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后爸业,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體其骄,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年扯旷,在試婚紗的時候發(fā)現(xiàn)自己被綠了拯爽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡钧忽,死狀恐怖毯炮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耸黑,我是刑警寧澤否副,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站崎坊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洲拇。R本人自食惡果不足惜奈揍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赋续。 院中可真熱鬧男翰,春花似錦、人聲如沸纽乱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸦列。三九已至租冠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間薯嗤,已是汗流浹背顽爹。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骆姐,地道東北人镜粤。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓捏题,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肉渴。 傳聞我的和親對象是個殘疾皇子公荧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容