Cache使用和Redis的使用:
一、準備工作:
新建一個springboot的project,然後配置好mybatis和druid的數據庫、數據源。
二、準備初試Cache緩存的作用:
1、新建一個bean.User實體類:
public class User {
private String username;
private String passwd;
private String phone;
private String address;
private String youbian;
public User() {
}
public User(String username, String passwd, String phone, String address, String youbian) {
this.username = username;
this.passwd = passwd;
this.phone = phone;
this.address = address;
this.youbian = youbian;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getYoubian() {
return youbian;
}
public void setYoubian(String youbian) {
this.youbian = youbian;
}
}
2、新建一個UserService服務:
@Service
public class UserService {
@Autowired
UserMapper userMapper;
/**
* 將方法的運行結果進行緩存,以後再要相同的數據,直接從緩存中取出數據
* 1、cacheNames/value 指定緩存的名字;
* 2、key: 緩存數據使用的key;可以用它來指定,默認就是使用方法參數的值: 1-方法的返回值
* 我們也可以使用SpEL: #username; 參數id的值 #a0 #po #root.args[0]
* 3、keyGenerator: key的生成器: 我們也可以使用自己自定key的生成器的組件username
* 4、key/keyGenerator 選一個使用
* 5、cacheMange 緩存管理器:我們可以指定緩存管理器
* 6、condition:指定符合條件的情況下才緩存;
* 7、unless:當unless指定的條件爲true,方法的返回值就不會被緩存;unless可以獲取到結果進行判斷:#result代表方法的返回值
* 8、sync:是否使用異步模式
*
* @param username
* @return
*/
//我們可以用 #root.args[0] 也代表使用username
@Cacheable(cacheNames = "user_emp",key = "#root.args[0]")
public User getUserName(String username){
System.out.println("查詢 --"+username+"-- 用戶");
return userMapper.getUserUsername(username);
}
public int insertUser(User user){
System.out.println("插入 --"+user.getUsername()+"-- 用戶");
return userMapper.insertUser(user);
}
}
@Cacheable註解作用:將方法的運行結果進行緩存,以後再要相同的數據,直接從緩存中取出數據。
1、cacheNames/value 指定緩存的名字;
2、key: 緩存數據使用的key;可以用它來指定,默認就是使用方法參數的值: 1-方法的返回值
我們也可以使用SpEL: #username; 參數id的值 #a0 #po #root.args[0]
3、keyGenerator: key的生成器: 我們也可以使用自己自定key的生成器的組件username
4、key/keyGenerator 選一個使用
5、cacheMange 緩存管理器:我們可以指定緩存管理器
6、condition:指定符合條件的情況下才緩存;
7、unless:當unless指定的條件爲true,方法的返回值就不會被緩存;unless可以獲取到結果進行判斷:#result代表方法的返回值
8、sync:是否使用異步模式。
3、新建一個Controller:
@RestController
public class UserController {
@Autowired
UserService userService;
@GetMapping("/user/{username}")
public User getUser(@PathVariable("username") String username){
return userService.getUserName(username);
}
@GetMapping("/user")
public User insertUser(User user){
int insertUser_id = userService.insertUser(user);
return user;
}
}
4、運行:
我們首次查詢的時候:控制檯會打印出sql語句。
但是我們第二次 第三次查詢這個數據的時候,控制檯不會再打印sql語句,這時我們已經把該數據放入到了緩存中,可以直接從緩存中獲取數據。
三、Cache緩存的原理:
1、自動配置類:CacheAutoConfiguration
2、緩存的配置類:
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
在CacheAutoConfiguration這個配置類中可以找到這個函數,我們可以用斷點來看看導入了哪些配置類。
導入了以下的這些包:
0 = "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
1 = "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
2 = "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
3 = "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
4 = "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
5 = "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
6 = "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
7 = "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
8 = "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"
9 = "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
3、哪個配置類默認生效呢?
我們可以直接在yml配置文件下添加debug=true打印自動配置報告。我們就可以看到有哪些生效了的配置類。
我們可以找到只生效了:SimpleCacheConfiguration
SimpleCacheConfiguration作用:給容器中註冊了一個CacheManager:ConcurrentMapCacheManager
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {
@Bean
ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
CacheManagerCustomizers cacheManagerCustomizers) {
//註冊了一個CacheManager
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManagerCustomizers.customize(cacheManager);
}
}
實現CacheManager後就可以有getCache方法了。
我們來看一下這個函數:
@Override
@Nullable
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
}
createConcurrentMapCache:
protected Cache createConcurrentMapCache(String name) {
SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null);
return new ConcurrentMapCache(name, new ConcurrentHashMap<>(256),
isAllowNullValues(), actualSerialization);
}
我們可以看到,這裏創建了一個Cache對象,並返回該對象。
4、所以這個SimpleCacheConfiguration所起作用就可以獲取和創建ConcurrentMapCache類型的緩存組件
我們來看一下new的這個ConcurrentMapCache類:
其中有一個lookup函數:
protected Object lookup(Object key) {
return this.store.get(key);
}
返回一個key的值。我們來看一下store是一個什麼結構:
private final ConcurrentMap<Object, Object> store;
store是一個Map鍵值對,根據傳入的key來獲取value值。
5、由以上分析,我們可以得到ConcurrentMapCache類型的緩存組件的作用:將數據保存在ConcurrentMap中,然後根據傳入的key來返回存入的value值
//查詢函數
@Override
@Nullable
protected Object lookup(Object key) {
return this.store.get(key);
}
//保存函數
@Override
public void put(Object key, @Nullable Object value) {
this.store.put(key, toStoreValue(value));
}
6、運行流程:
-
一、查詢緩存是否存在我們所請的數據,按照cacheName指定的名字獲取,如果第一次獲取緩存如果沒有Cache組件會自動創建;
-
二、去Cache中查找緩存的內容,使用一個key,默認方法的參數,key是按照某種策略生成的,默認是使用keyGenerator 生成的,默認使用SimpleKeyGenerator生成key。
SimpleKeyGenerator生成key的默認策略:- 如果沒有參數,key=new SimpleKey();
- 如果有一個參數,key=參數值;
- 如果有多個參數,key=new SimpleKey(params);
-
三、不存在的情況下,會調取對應的Service函數,然後調用Dao層,並返回數據;
-
四、拿到數據庫的數據後,會put到緩存中,如果設置了cacheNames,那麼就是以cacheNames會緩存名字,value就是請求的對象數據;
-
五、然後返回數據到前端頁面;
-
六、如果再次請求該數據,那麼就會在ConcurrentMap中查找到以傳入數據的值爲key的緩存,然後就從ConcurrentMap中取出數據並返回給前端頁面,不會執行Service函數,也不會訪問數據庫。
總體來說:
- 方法執行之前先來檢查緩存中有沒有數據,默認按照參數的值作爲key去查詢緩存,如果沒有就運行方法,並將結果放入緩存,以後再來調用就可以直接使用緩存中的數據。