歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Spring Cache緩存介紹

Spring Cache緩存介紹

日期:2017/2/28 13:48:58   编辑:Linux教程

Spring 3.1 引入了激動人心的基於注釋(annotation)的緩存(cache)技術,它本質上不是一個具體的緩存實現方案(例如 EHCache 或者 OSCache),而是一個對緩存使用的抽象,通過在既有代碼中添加少量它定義的各種 annotation,即能夠達到緩存方法的返回對象的效果。

Spring 的緩存技術還具備相當的靈活性,不僅能夠使用 SpEL(Spring Expression Language)來定義緩存的 key 和各種 condition,還提供開箱即用的緩存臨時存儲方案,也支持和主流的專業緩存例如 EHCache 集成。

其特點總結如下:

1.通過少量的配置 annotation 注釋即可使得既有代碼支持緩存

2.支持開箱即用 Out-Of-The-Box,即不用安裝和部署額外第三方組件即可使用緩存

3.支持 Spring Express Language,能使用對象的任何屬性或者方法來定義緩存的 key 和 condition

4.支持 AspectJ,並通過其實現任何方法的緩存支持

5.支持自定義 key 和自定義緩存管理者,具有相當的靈活性和擴展性

一、基於注解的支持

Spring為我們提供了幾個注解來支持Spring Cache。其核心主要是@Cacheable、@CachePut 和@CacheEvict。使用@Cacheable標記的方法在執行後Spring Cache將緩存其返回結果,@CachePut主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存,和 @Cacheable不同的是,它每次都會觸發真實方法的調用,而使用@CacheEvict標記的方法會在方法執行前或者執行後移除Spring Cache中的某些元素。

1.@Cacheable

@Cacheable可以標記在一個方法上,也可以標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類所有的方法都是支持緩存的。對於一個支持緩存的方法,Spring會在其被調用後將其返回值緩存起來,以保證下次利用同樣的參數來執行該方法時可以直接從緩存中獲取結果,而不需要再次執行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果,至於鍵的話,Spring又支持兩種策略,默認策略和自定義策略,需要注意的是當一個支持緩存的方法在對象內部被調用時是不會觸發緩存功能的。@Cacheable可以指定三個屬性,value、key和condition。

value:緩存的名稱,在 spring 配置文件中定義,必須指定至少一個。如@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}

key:緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合。如@Cacheable(value=”testcache”,key=”#userName”)

condition:緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行緩存。如@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

注:除了上述使用方法參數作為key之外,Spring還為我們提供了一個root對象可以用來生成key。通過該root對象我們可以獲取到以下信息。

屬性名稱

描述

示例

methodName

當前方法名

#root.methodName

method

當前方法

#root.method.name

target

當前被調用的對象

#root.target

targetClass

當前被調用的對象的class

#root.targetClass

args

當前方法參數組成的數組

#root.args[0]

caches

當前被調用的方法使用的Cache

#root.caches[0].name

2.@CachePut
在支持Spring Cache的環境下,對於使用@Cacheable標注的方法,Spring在每次執行前都會檢查Cache中是否存在相同key的緩存元素,如果存在就不再執行該方法,而是直接從緩存中獲取結果進行返回,否則才會執行並將返回結果存入指定的緩存中。@CachePut也可以聲明一個方法支持緩存功能。與@Cacheable不同的是使用@CachePut標注的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。
@CachePut也可以標注在類上和方法上。使用@CachePut時我們可以指定的屬性跟@Cacheable是一樣的。
3.@CacheEvict
@CacheEvict是用來標注在需要清除緩存元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執行都會觸發緩存的清除操作。@CacheEvict可以指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應的屬性類似。即value表示清除操作是發生在哪些Cache上的(對應Cache的名稱);key表示需要清除的是哪個key,如未指定則會使用默認策略生成的key;condition表示清除操作發生的條件。下面我們來介紹一下新出現的兩個屬性allEntries和beforeInvocation。
allEntries:是否清空所有緩存內容,缺省為 false,如果指定為 true,則方法調用後將立即清空所有緩存。如:@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation:是否在方法執行前就清空,缺省為 false,如果指定為 true,則在方法還沒有執行的時候就清空緩存,缺省情況下,如果方法執行拋出異常,則不會清空緩存。如:@CachEvict(value=”testcache”,beforeInvocation=true)
其他參數和@Cacheable相同
4.@Caching
@Caching注解可以讓我們在一個方法或者類上同時指定多個Spring Cache相關的注解。其擁有三個屬性:cacheable、put和evict,分別用於指定@Cacheable、@CachePut和@CacheEvict。如: @Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),@CacheEvict(value = "cache3", allEntries = true) })


二、實例

使用map集合實現緩存管理,演示spring cache的使用。

1.創建緩存對象實例


package org.springframework.cache.demo;

import java.io.Serializable;

//緩存對象
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private int id;
private String name;

public User(){
}

public User(String name){
this.name= name;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.對象服務實現類


package org.springframework.cache.demo;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;

/**
* 業務服務
*
*/
public class UserService {

@Cacheable(value = "userCache",key="#userName")
// 使用了一個緩存名叫 userCache
public User getUserByName(String userName) {
// 方法內部實現不考慮緩存邏輯,直接實現業務
return getFromDB(userName);
}

@CacheEvict(value = "userCache", key = "#user.name")
// 清空 accountCache 緩存
public void updateUser(User user) {
updateDB(user);
}

@CacheEvict(value = "userCache", allEntries = true,beforeInvocation=true)
// 清空 accountCache 緩存
public void reload() {
}

private User getFromDB(String userName) {
System.out.println("查詢數據庫..." + userName);
return new User(userName);
}

private void updateDB(User user) {
System.out.println("更新數據庫數據..." + user.getName());
}
}
3.緩存實現


package org.springframework.cache.demo.mycache;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;

public class MyCache implements Cache {

private String name;
private Map<String, Object> store = new ConcurrentHashMap<String, Object>();;

public MyCache() {
}

public MyCache(String name) {
this.name = name;
}

public void setName(String name) {
this.name = name;
}

public void clear() {
store.clear();
}

public void evict(Object obj) {
}

public ValueWrapper get(Object key) {
ValueWrapper result = null;
Object thevalue = store.get(key);
if (thevalue != null) {
result = new SimpleValueWrapper(thevalue);
}
return result;
}

public <T> T get(Object key, Class<T> clazz) {
return clazz.cast(store.get(key));
}

public String getName() {
return name;
}

public Object getNativeCache() {
return store;
}

public void put(Object key, Object value) {
store.put((String) key, value);
}

public ValueWrapper putIfAbsent(Object key, Object value) {
put(key, value);
return new SimpleValueWrapper(value);
}
}
4.spring配置

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 啟用緩存注解功能,這個是必須的,否則注解不會生效,另外,該注解一定要聲明在spring主配置文件中才會生效 -->
<cache:annotation-driven cache-manager="cacheManager" />

<bean id="userService" class="org.springframework.cache.demo.UserService" />

<!-- generic cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.demo.mycache.MyCache"
p:name="userCache" />
</set>
</property>
</bean>
</beans>5.運行類


package org.springframework.cache.demo.mycache;

import org.springframework.cache.demo.User;
import org.springframework.cache.demo.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyMain {

@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-cache-mycache.xml");
UserService userService = context.getBean(UserService.class);
// 第一次查詢,應該走數據庫
System.out.print("第一次查詢...");
userService.getUserByName("hello");
// 第二次查詢,應該不查數據庫,直接返回緩存的值
System.out.println("第二次查詢...");
userService.getUserByName("hello");
System.out.println();
System.out.println("==============");

// 更新某個記錄的緩存,首先構造兩個用戶記錄,然後記錄到緩存中
User user1 = userService.getUserByName("user1");
// 開始更新其中一個
user1.setId(1000);
userService.updateUser(user1);
// 因為被更新了,所以會查詢數據庫
userService.getUserByName("user1");
// 再次查詢,應該走緩存
userService.getUserByName("user1");
// 更新所有緩存
userService.reload();
System.out.println("清楚所有緩存");
// 查詢數據庫
userService.getUserByName("user1");
userService.getUserByName("user2");
// 查詢緩存
userService.getUserByName("user1");
userService.getUserByName("user2");
}
}
運行結果:


第一次查詢...查詢數據庫...hello
第二次查詢...

==============
查詢數據庫...user1
更新數據庫數據...user1
清楚所有緩存
查詢數據庫...user1
查詢數據庫...user2

Copyright © Linux教程網 All Rights Reserved