Spring的緩存抽象
Spring為不同的緩存提供了一層抽象。通過在Java的方法上面使用注解,加了注解的方法就會將該方法執行的結果緩存起來。在下一次使用相同參數調用該方法時就判斷如果是緩存過的就將緩存結果返回,如果是沒有緩存過的就會執行方法。所以Spring的緩存是基於AOP實現的。
Spring緩存注解
@EnableCaching
- 開啟緩存支持
@Cacheable
這個注解的作用負責將返回的數據進行緩存,當我們第一次訪問的時候,會將查出來的數據緩存起來,當之后在發起訪問的時候,會先去查看緩存中是否存在該條數據。該注解有如下介個參數支持:
| value | 緩存的名稱,在Spring配置文件中定義,必須指定至少一個 | 例如:
@Cacheable(value="mycache") |
---|
key |
@Cacheable(value="mycache",key="#userName") |
condition |
@Cacheable(value="mycache",condition="#userName.length()>2") |
@CacheEvict
清空指定的緩存,可以清空value,也可以清空value.key的數據。它支持的參數如下:
| value | 緩存的名稱,在Spring配置文件中定義,必須指定至少一個 | 例如:
@CachEvict(value="mycache") |
---|
key |
@CachEvict(value="mycache",key="#userName") |
condition |
@CachEvict(value="mycache",condition="#userName.length()>2") |
allEntries |
@CachEvict(value="mycache",allEntries=true) |
beforeInvocation |
@CachEvict(value="mycache",beforeInvocation=true) |
@CachePut
這個注解的作用和Cacheable類似,只不過它的作用相當於update。加上這個注解后,每次訪問數據都不會從緩存中拿取數據,而是從業務方法里查詢然后緩存。Cacheable和CachePut結合使用,因為當數據變化后,就需要將原來之前的數據給清空掉,否則的話就會產生臟數據。它的參數和Cacheable一樣。
| value | 緩存的名稱,在Spring配置文件中定義,必須指定至少一個 | 例如:
@CachePut(value="mycache") |
---|
key |
@CachePut(value="mycache",key="#userName") |
condition |
@CachePut(value="mycache",condition="#userName.length()>2") |
@Caching
這個注解就是聯合使用。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.cache.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
talk is cheap,show me the code
package com.lucky.spring.service.impl;
import com.lucky.spring.service.DataService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zhangdd on 2020/8/9
*/
@Service
public class DataServiceImpl implements DataService {
@Cacheable(value = "person")
@Override
public List<String> findAllName() {
//模擬從數據庫查詢數據
List<String> data = new ArrayList<>();
data.add("張飛");
data.add("趙雲");
data.add("許褚");
return data;
}
@CacheEvict(value = "person")
@Override
public void reloadPerson(){
}
}
- findAllName()方法模擬從數據庫讀取數據,並使用@Cacheable注解表明查詢結果會緩存起來
- reloadPerson()方法,用來清理緩存
package com.lucky.spring;
import com.lucky.spring.service.DataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
@Slf4j
public class Application implements CommandLineRunner {
@Autowired
private DataService dataService;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info("count {}", dataService.findAllName().size());
for (int i = 0; i < 5; i++) {
log.info("再次執行查詢");
dataService.findAllName();
}
dataService.reloadPerson();
log.info("read after refresh");
dataService.findAllName().forEach(p -> {
log.info("person:{}", p);
});
}
}
打印結果如下:
2020-08-09 15:17:31.564 INFO 63660 --- [ main] c.l.spring.service.impl.DataServiceImpl : mock query from db
2020-08-09 15:17:31.565 INFO 63660 --- [ main] com.lucky.spring.Application : count 3
2020-08-09 15:17:31.565 INFO 63660 --- [ main] com.lucky.spring.Application : 再次執行查詢
2020-08-09 15:17:31.565 INFO 63660 --- [ main] com.lucky.spring.Application : 再次執行查詢
2020-08-09 15:17:31.566 INFO 63660 --- [ main] com.lucky.spring.Application : 再次執行查詢
2020-08-09 15:17:31.566 INFO 63660 --- [ main] com.lucky.spring.Application : 再次執行查詢
2020-08-09 15:17:31.566 INFO 63660 --- [ main] com.lucky.spring.Application : 再次執行查詢
2020-08-09 15:17:31.568 INFO 63660 --- [ main] c.l.spring.service.impl.DataServiceImpl : clear cache data
2020-08-09 15:17:31.568 INFO 63660 --- [ main] com.lucky.spring.Application : read after refresh
2020-08-09 15:17:31.568 INFO 63660 --- [ main] c.l.spring.service.impl.DataServiceImpl : mock query from db
2020-08-09 15:17:31.568 INFO 63660 --- [ main] com.lucky.spring.Application : person:張飛
2020-08-09 15:17:31.568 INFO 63660 --- [ main] com.lucky.spring.Application : person:趙雲
2020-08-09 15:17:31.568 INFO 63660 --- [ main] com.lucky.spring.Application : person:許褚
- 首先查詢得到數據集合的大小,打印了 service方法里的內容
mock query from db
- 在繼續循環中調用該方法,就沒有在執行了,而是從緩存中獲取到的數據
- 執行清理緩存后,再次查詢,可以看到 service 方法里的內容又被執行打印
使用Spring的緩存抽象時使用Redis作為其緩存
直接使用Spring的緩存抽象,緩存數據是被緩存在JVM中的。在集群場景下,不同的節點上緩存的數據就會又不一致性。
添加相關依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
配置cache屬性
spring.cache.type=redis
spring.cache.cache-names=person
#有效期5s
spring.cache.redis.time-to-live=5000
spring.cache.redis.cache-null-values=false
spring.redis.host=localhost
其他不變,看下redis里存儲的數據。到這里 數據已經緩存到redis了。