springboot17-Cache的使用和原理

Cache使用和Redis的使用:

一、準備工作:

新建一個springboot的project,然後配置好mybatisdruid的數據庫、數據源。

二、準備初試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、運行:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-WmDO0Ozo-1584289740357)(C:\Users\ouguangji\AppData\Roaming\Typora\typora-user-images\image-20200315230940974.png)]

我們首次查詢的時候:控制檯會打印出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這個配置類中可以找到這個函數,我們可以用斷點來看看導入了哪些配置類。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KvBeziqt-1584289740361)(C:\Users\ouguangji\AppData\Roaming\Typora\typora-user-images\image-20200315231644136.png)]

導入了以下的這些包:

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去查詢緩存,如果沒有就運行方法,並將結果放入緩存,以後再來調用就可以直接使用緩存中的數據。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章