1 簡介
Spring從3.1版本開始,提供Cache和CacheManager接口實現數據緩存功能,支持使用註解簡化開發。Cache接口爲緩存的組件規範定義,包含緩存的各種操作集合,CacheManager接口提供緩存配置管理。
本文將介紹Cache註解的使用方法和場景。我們可以爲需要緩存功能的方法加上Cache註解,每次調用方法時,Spring會檢查指定參數的指定目標方法是否已經被調用過,如果有就直接從緩存中獲取結果,如果沒有就調用方法並緩存結果後返回給用戶,下次調用即可直接從緩存中獲取。
2 註解定義
Cache的常用註解及定義如下:
名稱 | 定義 |
---|---|
@EnableCaching | 用於啓動類或配置類,開啓使用基於註解的緩存。 |
@Cacheable | 能夠根據方法的請求參數和返回結果,定義鍵值進行緩存。緩存存在時直接返回結果不調用方法,常用於查詢。 |
@CachePut | 保證方法被調用,又希望結果被緩存。與@Cacheable區別在於每次都調用方法,常用於寫入或更新。 |
@CacheEvict | 用於刪除或清空緩存。 |
@Caching | 組合多個註解標籤。 |
@CacheConfig | 用於使用緩存的類,統一配置緩存註解的屬性。 |
註解的主要屬性及定義如下:
名稱 | 定義 |
---|---|
value | 緩存的名稱,在spring配置文件中定義,必須指定至少一個。value指定了緩存存放的命名空間,所有寫入和刪除緩存的操作均針對指定命名空間的緩存。例如:@Cacheable(value = ”myCache”) ,或@Cacheable(value = {”myCache1”, ”myCache2”} |
key | 緩存的key,按照SpEL表達式編寫,例如:@Cacheable(value = ”myCache”, key = ”#id”) 。如果不指定,則按照方法的參數進行組合。 |
condition | 緩存的條件,可以爲空,使用SpEL編寫,返回true或者false,只有爲true才緩存數據或清除緩存。例如:@Cacheable(value = ”myCache”, condition = ”#id.length() > 0”) |
unless | 否定條件的緩存,返回結果爲true時不緩存。例如:@Cacheable(value = ”myCache”, unless = ”#id == null || id.length() == 0”) |
allEntries | 是否清空所有緩存內容,@CacheEvict註解使用,缺省爲false,如果指定爲true,則方法調用後將立即清空所有緩存。例如:@CachEvict(value = ”myCache”, allEntries = true) |
beforeInvocation | 是否在方法執行前清空緩存,@CacheEvict註解使用,缺省爲 false,如果指定爲 true,則在執行方法前就清空緩存。缺省情況下,如果方法執行拋出異常,則不會清空緩存。例如:@CachEvict(value = ”myCache”,beforeInvocation = true) |
sync | 爲true時,只有一個線程的請求會到達數據庫,其他線程都會等待直到緩存可用。這個設置可減少對數據庫的瞬間併發訪問。 |
當一個類需要使用緩存的方法較多時,可在該類上使用@CacheConfig註解來統一指定value的值,之後可省略value配置。如果在方法依舊寫上value,那麼以方法的value值爲準。示例如下:
@CacheConfig(cacheNames = {"myCache"})
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
@Service
public class UserServiceImpl implements UserService {
}
多個Cache註解組合使用時,需要用@Caching組合多個註解標籤,示例如下:
@Caching(cacheable = {
@Cacheable(value = "emp",key = "#p0"),
...
},
put = {
@CachePut(value = "emp",key = "#p0"),
...
},evict = {
@CacheEvict(value = "emp",key = "#p0"),
....
})
public User save(User user) {
....
}
3 SpEL上下文
SpEL的常用運算符如下:
類型 | 運算符 |
---|---|
關係 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算術 | +,- ,* ,/,%,^ |
邏輯 | &&,||,!,and,or,not,between,instanceof |
條件 | ?: (ternary),?: (elvis) |
正則表達式 | matches |
其他類型 | ?.,?[…],![…],^[…],$[…] |
Cache有一些SpEL上下文數據供配置緩存規則使用,如下表:
名稱 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root對象 | 當前被調用的方法名 | #root.methodName |
method | root對象 | 當前被調用的方法 | #root.method.name |
target | root對象 | 當前被調用的目標對象實例 | #root.target |
targetClass | root對象 | 當前被調用的目標對象的類 | #root.targetClass |
args | root對象 | 當前被調用的方法的參數列表 | #root.args[0] |
caches | root對象 | 當前方法使用的緩存列表 | #root.caches[0].name |
Argument Name | 執行上下文 | 當前被調用的方法的參數,如:getUser(User user) ,可以通過#user.id 獲得參數 |
#user.id |
result | 執行上下文 | 方法執行後的返回值,僅對於方法執行後的判斷有效,如使用unless或beforeInvocation = false | #result |
SpEL使用方法參數時,可以直接使用“#參數名”或者“#p+參數index”。 如:
@Cacheable(value = "myCache", key = "#id")
@Cacheable(value = "myCache", key = "#p0")
使用root對象的屬性作爲key時,也可以將#root省略,因爲Spring默認使用的就是root對象的屬性。如:
@Cacheable(key = "targetClass + methodName + #p0")
4 應用
使用Cache註解需要先在pom.xml文件添加依賴:
<!-- 引入緩存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
然後在啓動類或配置類增加@EnableCaching註解,開啓緩存,例如:
@SpringBootApplication
@EnableCaching
public class DataServerApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DataServerApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(DataServerApplication.class);
}
}
接下來就可以在代碼中使用Cache註解實現緩存操作了,示例代碼如下:
/**
* 根據account獲取用戶信息
*
* @param user
* @return
*/
@Override
@Cacheable(value = "userCache", key = "#user.account", sync = true)
public User getUserByAccount(User user) {
if (user != null) {
// 緩存不存在,從數據庫獲取
return userMapper.getUserByAccount(user.getAccount());
}
return null;
}
/**
* 添加用戶
*
* @param user
* @return
*/
@Override
@CachePut(value = "userCache", key = "#user.account", unless = "#result == null")
public User insertUser(User user) {
if (user != null) {
Date now = new Date();
user.setCreateTime(now);
user.setModifyTime(now);
user.setDeleted(0);
int result = userMapper.insert(user);
if (result > 0) {
return user;
}
}
return null;
}
/**
* 根據id更新用戶信息
*
* @param user
* @return
*/
@Override
@CachePut(value = "userCache", key = "#user.account", unless = "#result == null")
public User updateUser(User user) {
if (user != null) {
int result = userMapper.updateById(user);
if (result > 0) {
return user;
}
}
return null;
}
/**
* 根據id刪除用戶
*
* @param user
* @return
*/
@Override
@CacheEvict(value = "userCache", key = "#user.account")
public int deleteUser(User user) {
if (user != null) {
user.setDeleted(1);
return userMapper.updateById(user);
}
return 0;
}
對象存入緩存前需要進行json序列化,所以示例代碼中的User實體類一定要實現序列化public class User implements Serializable
,否則會報java.io.NotSerializableException異常。
設置了value值後,根據Cache默認的key生成策略,存入緩存中key的值爲name + “::” + key,例如按照示例代碼的配置,當account=user1時,緩存中key的值爲myCache::user1。