概述
??Spring提供了對應(yīng)用程序添加緩存的支持灾馒。從本質(zhì)上講笼呆,將緩存應(yīng)用于方法上,從而根據(jù)緩存中的信息減少執(zhí)行次數(shù)。當(dāng)開發(fā)者調(diào)用一個方法時鸥跟,將方法的參數(shù)和返回值作為Key/Value緩存起來丢郊,當(dāng)再次調(diào)用這個方法時,如果緩存中存在對應(yīng)數(shù)據(jù)医咨,就從緩存中獲取數(shù)據(jù)枫匾,否則再去執(zhí)行該方法。
支持
Spring并沒有提供緩存的實現(xiàn)拟淮,而是提供了一套Api干茉,可以自由選擇緩存的實現(xiàn)。目前Spring Boot支持的緩存有如下幾種(Spring Boot會按順序檢測以下提供的程序):
- Generic
- JCache(JSR-107)(EhCache 3, Hazelcast, Infinispan, and others)
- EhCache 2.x
- Hazelcast
- Infinispan
- Couchbase
- Readis
- Caffeine
- Simple
可以通過設(shè)置屬性來指定提供緩存的程序很泊。
無論使用哪種緩存實現(xiàn)不同的只是緩存配置角虫,使用的緩存注解是一致的
注解 | 介紹 |
---|---|
@EnableCaching | 啟用S??pring的注釋驅(qū)動的緩存管理功能 |
@CacheConfig | 當(dāng)此批注出現(xiàn)在給定的類上時,它為該類中定義的任何緩存操作提供一組默認(rèn)設(shè)置委造。 |
@Cacheable | 表示可以緩存調(diào)用方法(或類中的所有方法)結(jié)果的注釋戳鹅。每次調(diào)用指定方法時,將進(jìn)行緩存行為昏兆,檢查是否已緩存該方法參數(shù)和結(jié)果枫虏。如果在緩存中找不到鍵的值,則將調(diào)用目標(biāo)方法并將返回的值存儲在關(guān)聯(lián)的緩存中爬虱。 |
@CacheEvict | 表示方法(或類上的所有方法)觸發(fā)緩存逐出操作的注解隶债。 |
@CachePut | 表示方法(或類上的所有方法)觸發(fā)緩存放置操作的注釋。 |
@Caching | 此批注可用作元批注跑筝,以創(chuàng)建具自定義組合批注死讹。 |
一、Ehcache 2.x 緩存
在項目的pom.xml文件中添加以下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
添加緩存配置文件
如果存在Ehcache的依賴曲梗,并且在classpath下有名為ehcache.xml的文件回俐,那么EhCacheCacheManager將會自動作為緩存的提供者。因此稀并,在resources目錄下創(chuàng)建ehcache.xml文件作為Ehcach緩存的配置文件仅颇。
如果未配置ehcache.xml,則Ehcache依賴包下的ehcache-failsafe.xml是ehcache的默認(rèn)配置碘举。
<ehcache>
<!--diskStore元素是可選的忘瓦。-->
<!--如果為任何緩存啟用了overflowToDisk或diskPersistent,則必須對其進(jìn)行配置。-->
<!--diskStore只有一個屬性 - path耕皮。這是將在其中創(chuàng)建.data和.index文件的目錄路徑境蜕。-->
<!--如果路徑是Java系統(tǒng)屬性,則將其替換為正在運行的VM中的值-->
<!--user.home - 用戶的主目錄-->
<!--user.dir - 用戶的當(dāng)前工作目錄-->
<!--java.io.tmpdir - 默認(rèn)臨時文件路徑-->
<!--ehcache.disk.store.dir - 在命令行上指定的系統(tǒng)屬性(例:java -Dehcache.disk.store.dir=/u01/myapp/diskdir)-->
<!--子目錄可以在屬性后指定 例:java.io.tmpdir/cache-->
<diskStore path="java.io.tmpdir/cache"/>
<!--強(qiáng)制性默認(rèn)緩存配置-->
<!--這些設(shè)置將應(yīng)用于使用CacheManager.add(String cacheName)創(chuàng)建的緩存凌停。-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<!--localTempSwap - 該策略允許緩存在緩存操作期間使用本地磁盤粱年。磁盤存儲是臨時的,并在重新啟動后清除罚拟。-->
<!--當(dāng)策略為"none"時台诗,所有緩存都保留在內(nèi)存中(磁盤無溢出,磁盤無持久性)赐俗。-->
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="student"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="600"/>
</ehcache>
緩存配置 <cache/>:
屬性 | 介紹 |
---|---|
name | 設(shè)置緩存的名稱拉队,用于標(biāo)識緩存。 |
maxElementsInMemory | 設(shè)置在內(nèi)存中創(chuàng)建的最大對象數(shù)(0 ==無限制)阻逮。 |
maxElementsOnDisk | 設(shè)置將在DiskStore中維護(hù)的最大對象數(shù)粱快。默認(rèn)值為零,表示無限制叔扼。 |
eternal | 設(shè)置元素是否一直存在事哭。如果為true,超時將被忽略瓜富。 |
overflowToDisk | 設(shè)置當(dāng)內(nèi)存中的緩存達(dá)到maxInMemory限制時元素是否可以溢出到磁盤慷蠕。 |
timeToIdleSeconds | 設(shè)置元素過期之前的空閑時間,即緩存創(chuàng)建以后最后一次訪問緩存的時間到超時失效時的時間間隔食呻。值為0表示無窮大流炕,默認(rèn)值為0。 |
timeToLiveSeconds | 設(shè)置元素過期之前的生存時間仅胞,即從創(chuàng)建時間到元素過期之間的間隔每辟。值為0表示一直存在,默認(rèn)值為0干旧。 |
diskPersistent | 磁盤存儲在虛擬機(jī)重新啟動后是否仍然存在渠欺,默認(rèn)值為false。 |
diskExpiryThreadIntervalSeconds | 做元素失效監(jiān)測以及清除工作的線程運行間隔時間椎眯,默認(rèn)值為120秒挠将。 |
diskSpoolBufferSizeMB | 磁盤緩沖區(qū)大小,默認(rèn)30MB(內(nèi)部以字節(jié)為單位编整,設(shè)置的值轉(zhuǎn)換為字節(jié)后不可超過正整數(shù)表示范圍)舔稀。 |
memoryStoreEvictionPolicy | 達(dá)到maxElementsInMemory限制后,將強(qiáng)制執(zhí)行清除策略掌测。默認(rèn)策略是最近最少使用(LRU)内贮,其他可用策略先進(jìn)先出(指定為FIFO)和不常用(指定為LFU)。 |
開啟緩存
在項目的入口類上添加@EnableCaching注解開啟緩存。
@EnableCaching
@SpringBootApplication
public class EhcacheApplication {
public static void main(String[] args) {
SpringApplication.run(EhcacheApplication.class, args);
}
}
創(chuàng)建數(shù)據(jù)接口
@Service
@CacheConfig(cacheNames = "student")
public class StudentService {
@Resource
private StudentMapper studentMapper;
@Cacheable
public Student findById(Integer id) {
Student student = studentMapper.findById(id);
System.out.println("查詢 :" + JSON.toJSONString(student));
return student;
}
@CachePut(key = "#student.id")
public Student insert(Student student) throws Exception {
int status = studentMapper.insert(student);
if (status == 0) {
throw new Exception("Insert failed");
}
System.out.println("插入 :" + JSON.toJSONString(student));
return student;
}
@CacheEvict(key = "#student.id")
public Student delete(Student student) throws Exception {
int status = studentMapper.delete(student);
if (status == 0) {
throw new Exception("Delete failed");
}
System.out.println("刪除 :" + JSON.toJSONString(student));
return student;
}
@CachePut(key = "#student.id")
public Student update(Student student) throws Exception {
int status = studentMapper.update(student);
if (status == 0) {
throw new Exception("Update failed");
}
System.out.println("更新 :" + JSON.toJSONString(student));
return student;
}
}
創(chuàng)建請求接口
@RestController
@RequestMapping("/std")
public class StudentController {
@Resource
private StudentService studentService;
/**
* 查詢
*/
@RequestMapping("/sel")
public String findById(Student student) {
Map<String, Object> msg = new HashMap<>();
try {
student = studentService.findById(student.getId());
msg.put("status", 200);
msg.put("result", student);
} catch (Exception e) {
e.printStackTrace();
}
return JSON.toJSONString(msg);
}
/**
* 插入
*/
@RequestMapping("/ins")
public String insert(Student student) {
Map<String, Object> msg = new HashMap<>();
try {
studentService.insert(student);
msg.put("status", 200);
msg.put("message", "插入成功");
} catch (Exception e) {
msg.put("status", 400);
msg.put("message", "插入失敗");
}
return JSON.toJSONString(msg);
}
/**
* 刪除
*/
@RequestMapping("/del")
public String delete(Student student) {
Map<String, Object> msg = new HashMap<>();
try {
studentService.delete(student);
msg.put("status", 200);
msg.put("message", "刪除成功");
} catch (Exception e) {
msg.put("status", 400);
msg.put("message", "刪除失敗");
}
return JSON.toJSONString(msg);
}
/**
* 更新
*/
@RequestMapping("/upd")
public String update(Student student) {
Map<String, Object> msg = new HashMap<>();
try {
studentService.update(student);
msg.put("status", 200);
msg.put("message", "更新成功");
} catch (Exception e) {
msg.put("status", 400);
msg.put("message", "更新失敗");
}
return JSON.toJSONString(msg);
}
}
測試
向數(shù)據(jù)庫中添加數(shù)據(jù):
(一) 查詢
連續(xù)訪問查詢接口頁面并觀察輸出信息:
根據(jù)輸出信息發(fā)現(xiàn)夜郁,在瀏覽器中多次獲取數(shù)據(jù)什燕,數(shù)據(jù)查詢方法只執(zhí)行了一遍。
(二) 更新
訪問更新接口:
再次訪問查詢接口:
觀察控制臺:
發(fā)現(xiàn)訪問更新接口后再次訪問查詢接口竞端,查詢接口并沒有再次從數(shù)據(jù)庫獲取數(shù)據(jù)屎即,而是從緩存中獲取,所以更新接口返回的結(jié)果會覆蓋指定參數(shù)鍵的緩存事富。
(三) 插入
訪問插入接口:
訪問查詢接口:
觀察控制臺:
發(fā)現(xiàn)插入數(shù)據(jù)后再次查詢并沒有從數(shù)據(jù)庫中獲取數(shù)據(jù)技俐,而是從緩存中獲取。因為插入時對參數(shù)和返回的結(jié)果進(jìn)行了緩存赵颅。
(四) 刪除
訪問刪除接口:
訪問查詢接口:
觀察控制臺:
發(fā)現(xiàn)訪問刪除接口后數(shù)據(jù)庫中的數(shù)據(jù)被刪除虽另,再次訪問查詢接口進(jìn)行查詢發(fā)現(xiàn)觸發(fā)了查詢方法暂刘,說明刪除數(shù)據(jù)時緩存同時也被刪除了饺谬。
二、Redis 單機(jī)緩存
和Ehcache一樣谣拣,如果在classpath下存在Redis并且Redis已經(jīng)配置好募寨,此時會默認(rèn)使用RedisCacheManager作為緩存的提供者。
在項目的pom.xml文件中添加以下依賴(與之前的依賴相比不同的只是緩存的提供者):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
添加項目配置:
spring:
datasource:
url: jdbc:mysql://xxx.xxx.xxx.xxx/user
username: xxxx
password: xxxxxx
# 緩存配置
cache:
# 配置緩存名森缠,多個緩存可使用逗號分隔(one,two)
cache-names: student
# 緩存存在時間
redis:
time-to-live: 120s
redis:
host: xxx.xxx.xxx.xxx
port: xxxx
之后的流程與之前一致就不在闡述了拔鹰。