一、什麼是緩存
緩存就是資料交換的緩沖區(稱作:Cache),他把一些外存上的資料儲存在記憶體上,為什麼儲存在記憶體上,我們運作的所有程式裡面的變量都是存放在記憶體中的,是以如果想将值放入記憶體上,可以通過變量的方式存儲。在JAVA中一些緩存一般都是通過Map集合來實作的。
緩存在不同的場景下,作用是不一樣的具體舉例說明:
✔ 作業系統磁盤緩存 ——> 減少磁盤機械操作。
✔ 資料庫緩存——>減少檔案系統IO。
✔ 應用程式緩存——>減少對資料庫的查詢。
✔ Web伺服器緩存——>減少應用伺服器請求。
✔ 用戶端浏覽器緩存——>減少對網站的通路。
具體關于緩存的詳細介紹以及緩存的面試問題可以參考這篇部落格
https://blog.csdn.net/zhengzhaoyang122/article/details/82184029二、Spring緩存抽象
Spring從3.1開始定義了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口來統一不同的緩存技術;并支援使用JCache(JSR-107)注解簡化我們開發;
每次調用需要緩存功能的方法時,Spring會檢查檢查指定參數的指定的目标方法是否已經被調用過;如果有就直接從緩存中擷取方法調用後的結果,如果沒有就調用方法并緩存結果後傳回給使用者。下次調用直接從緩存中擷取。
使用Spring緩存抽象時我們需要關注以下兩點;
1、确定方法需要被緩存以及他們的緩存政策
2、從緩存中讀取之前緩存存儲的資料

三、幾個重要概念&緩存注解
1、Cache:
緩存接口,定義緩存操作。實作有:RedisCache、EhCacheCache、ConcurrentMapCache等
2、CacheManager
緩存管理器,管理各種緩存(Cache)元件
3、@Cacheable
主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存
(後面會細說該注解)
4、@CacheEvict
清空緩存
5、@CachePut
保證方法被調用,又希望結果被緩存。
6、@EnableCaching
開啟基于注解的緩存
7、keyGenerator
緩存資料時key生成政策
8、serialize
緩存資料時value序列化政策
9、@Cacheable/@CachePut/@CacheEvict 主要的參數
四、springboot整合redis實作緩存
1、引入依賴
在pom.xml中引入spring-boot-starter-data-redis依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置redis連接配接位址
在application.yml或者application.properties中配置redis連接配接位址
這裡還需要配置一下資料庫的位址,友善測試使用
application.properties配置
spring.datasource.url=jdbc:mysql://MySQL的主機位址:3306/資料庫名
spring.datasource.username=root
spring.datasource.password=密碼
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 啟用mybatis的命名政策(即駝峰命名法)
mybatis.configuration.map-underscore-to-camel-case=true
logging.level.com.atguigu.cache.mapper=debug
debug=true
spring.redis.host=redis主機位址
application.yml配置:
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://MySQL的主機位址:3306/資料庫名
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #配置Druid資料源
# 資料源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用于防火牆
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
redis:
host: redis主機位址
#mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
# 開啟駝峰命名
mybatis:
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.canghe.springboot.mapper: debug
debug: true
# mapper-locations: classpath:mybatis/mapper/*.xml
3、在虛拟機或者雲伺服器上安裝redis
可以直接通過docker安裝redis,并配置端口
4、使用RestTemplate操作redis
@Autowired
StringRedisTemplate stringRedisTemplate;//操作k-v都是字元串
@Autowired
RedisTemplate redisTemplate;//操作k-v對象的
Redis常見的五大資料類型
String(字元串) 、list(清單)、set(集合)、hash(散列)、Zset(有序集合)
- redisTemplate.opsForValue();//操作字元串
- redisTemplate.opsForHash();//操作hash
- redisTemplate.opsForList();//操作list
- redisTemplate.opsForSet();//操作set
- redisTemplate.opsForZSet();//操作有序set
測試代碼:
@Test
public void test01() {
// stringRedisTemplate.opsForValue().append("key","helloword");
// String msg = stringRedisTemplate.opsForValue().get("key");
// System.out.println("msg:"+msg);
stringRedisTemplate.opsForList().leftPush("firstList","1");
stringRedisTemplate.opsForList().leftPush("firstList","2");
}
五、@Cacheable注解
1、運作流程
1、方法運作之前,先去查詢Cache(緩存元件),按照cacheNames指定的名字擷取;(CacheManager先擷取相應的緩存),第一次擷取緩存如果沒有Cache元件會自動建立。
2、去Cache中查找緩存的内容,使用一個key,預設就是方法的參數; key是按照某種政策生成的;預設是使用keyGenerator生成的,預設使用SimpleKeyGenerator生成key; SimpleKeyGenerator生成key的預設政策;
如果沒有參數;key=new SimpleKey();
如果有一個參數:key=參數的值
如果有多個參數:key=new SimpleKey(params);
3、沒有查到緩存就調用目标方法;
4、将目标方法傳回的結果,放進緩存中
@Cacheable标注的方法執行之前先來檢查緩存中有沒有這個資料,預設按照參數的值作為key去查詢緩存,**
如果沒有就運作方法并将結果放入緩存;以後再來調用就可以直接使用緩存中的資料;**
2、核心
1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】元件
2)、key使用keyGenerator生成的,預設是SimpleKeyGenerator
3、幾個屬性
- cacheNames/value:指定緩存元件的名字;将方法的傳回結果放在哪個緩存中,是數組的方式,可以指定多個緩存;
- key:緩存資料使用的key;可以用它來指定。預設是使用方法參數的值 1-方法的傳回值, 編寫SpEL; #i d;參數id的值 #a0 #p0 #root.args[0] getEmp[2]
- keyGenerator:key的生成器;可以自己指定key的生成器的元件id,key/keyGenerator:二選一使用;
- cacheManager:指定緩存管理器;或者cacheResolver指定擷取解析器
- condition:指定符合條件的情況下才緩存; condition = "#a0>1":第一個參數的值》1的時候才進行緩存
- unless:否定緩存;當unless指定的條件為true,方法的傳回值就不會被緩存;可以擷取到結果進行判斷
unless = "#result == null" * unless = "#a0==2":如果第一個參數的值是2,結果不緩存;
- sync:是否使用異步模式
@Cacheable例子:
@Cacheable(value = {"emp"}/*,keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2"*/)
public Employee getEmp(Integer id){
System.out.println("查詢"+id+"号員工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
六、@CachePut
1、啟用場景
既調用方法,又更新緩存資料;同步更新緩存,修改了資料庫的某個資料,同時更新緩存;
2、運作時機
- 先調用目标方法
- 将目标方法的結果緩存起來
3、測試步驟
1、查詢1号員工;查到的結果會放在緩存中;
key:1 value:lastName:張三
2、以後查詢還是之前的結果
3、更新1号員工;【lastName:zhangsan;gender:0】
将方法的傳回值也放進緩存了;
key:傳入的employee對象 值:傳回的employee對象;
4、查詢1号員工?
應該是更新後的員工;
key = "#employee.id":使用傳入的參數的員工id;
key = "#result.id":使用傳回後的id
@Cacheable的key是不能用#result
為什麼是沒更新前的?【1号員工沒有在緩存中更新】
@CachePut(/*value = "emp",*/key = "#result.id")
public Employee updateEmp(Employee employee){
System.out.println("updateEmp:"+employee);
employeeMapper.updateEmp(employee);
return employee;
}
七、@CacheEvict
緩存清除
/**
* @CacheEvict:緩存清除
* key:指定要清除的資料
* allEntries = true:指定清除這個緩存中所有的資料
* beforeInvocation = false:緩存的清除是否在方法之前執行
* 預設代表緩存清除操作是在方法執行之後執行;如果出現異常緩存就不會清除
*
* beforeInvocation = true:
* 代表清除緩存操作是在方法運作之前執行,無論方法是否出現異常,緩存都清除
*
*
*/
@CacheEvict(value="emp",beforeInvocation = true/*key = "#id",*/)
public void deleteEmp(Integer id){
System.out.println("deleteEmp:"+id);
//employeeMapper.deleteEmpById(id);
int i = 10/0;
}
八、@Caching
定義複雜的緩存規則
@Caching(
cacheable = {
@Cacheable(/*value="emp",*/key = "#lastName")
},
put = {
@CachePut(/*value="emp",*/key = "#result.id"),
@CachePut(/*value="emp",*/key = "#result.email")
}
)
The more effort,the more lucky
https://github.com/freestylefly/javaStudy https://github.com/freestylefly/javaStudy https://github.com/freestylefly/javaStudyGitHub項目
https://github.com/freestylefly/javaStudyhttps://github.com/freestylefly/javaStudy