------------------------------------------------------------------------------
開門見代碼:
[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
指定緩存的類型
配置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);
? ? }
}
一般來講我們使用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ù)。
得益于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)大和靈活的多了拧略。