Spring緩存支持

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

開門見代碼:

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


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

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

使用具體的代碼介紹相關注解及其屬性的用法。

@Cacheable,

@CacheEvict,

@CachePut,

@CacheConfig


子項目 cache 即是 Spring緩存的演示項目,相關sql在cache子項目的根目錄,

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

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

CacheManager描述

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

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

EhCacheCacheManager使用EhCache作為緩存技術

GuavaCacheManager使用Google Guava的GuavaCache作為緩存技術

JCacheCacheManager使用JCache(JSR-107)標準的實現(xiàn)作為緩存技術,比如Apache Commons JCS

RedisCacheManager使用Redis作為緩存技術

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

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

/**

* EhCache的配置

*/

@Bean

public EhCacheCacheManagercacheManager(CacheManager cacheManager) {

? ? return new EhCacheCacheManager(cacheManager);

}

當然,各種緩存技術都有很多其他配置,但是配置cacheManager是必不可少的。

聲明式緩存注解

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

注解? ?說明

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

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

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

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

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

2.?集成Redis緩存

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

2.1?安裝redis

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

2.2?添加maven依賴

? ? org.springframework.boot

? ? spring-boot-starter-data-redis

2.3?配置application.yml

指定緩存的類型

配置redis的服務器信息

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,可以實現(xiàn)keyGenerator。

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

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

/**

* 自定義緩存key的生成類實現(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));

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

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

? ? ? ? }

? ? };

}

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

4.?使用

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

@Service

@CacheConfig(cacheNames="users")

public class UserService {

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

? ? @Resource

? ? private UserMapper userMapper;

? ? /**

? ? * cacheNames 設置緩存的值

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

*@paramid

*@return

? ? */

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

? ? public UsergetById(int id) {

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

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

? ? }

? ? /***

? ? * 如果設置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);

? ? }


? ? /**

? ? * 以上我們使用默認的keyGenerator,對應spring的SimpleKeyGenerator

? ? * 如果你的使用很復雜,我們也可以自定義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ù)。

切換緩存技術

得益于SpringBoot的自動配置機制,切換緩存技術除了替換相關maven依賴包和配置Bean外,使用方式和實例中一樣,不需要修改業(yè)務代碼。如果你要切換到其他緩存技術非常簡單。

EhCache

當我們需要使用EhCache作為緩存技術的時候,只需要在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

當我們需要Guava作為緩存技術的時候,只需要在pom.xml中增加Guava的依賴即可:

? ? com.google.guava

? ? guava

? ? 18.0

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

Redis

最后還提一點,本篇采用Redis作為緩存技術,添加了依賴:

? ? org.springframework.boot

? ? spring-boot-starter-data-redis

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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容