springboot 緩存@EnableCaching實例
springboot 緩存@EnableCaching
很多時候系統的瓶頸都在一些比較復雜的IO操作,例如讀取數據庫,如果一些比較穩定的數據,一般的解決方案就是用緩存。spring boot提供瞭比較簡單的緩存方案。隻要使用 @EnableCaching即可完成簡單的緩存功能。
緩存的實現有多種實現,ConcurentHashMapCache , GuavaCache, EnCacheCache等多種實現,spring boot 有默認的實現。本文不深入源碼解讀,首先用起來。
此處我們模擬要緩存的User
class User { private Long id; private String name; // setter getter }
然後我們業務對象:
import javax.annotation.PostConstruct; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.EnableCaching; import org.springframework.stereotype.Component; /** * @author micro * @date 2017年8月2日 * @description : */ @Component @EnableCaching public class UserDao { private Map<Long, User> userMap; @PostConstruct public void init() { //模擬數據庫 userMap = new HashMap<Long, User>(); userMap.put(1L, new User(1L,"micro1")); userMap.put(2L, new User(2L, "micro2")); } @Cacheable("user") // 註解key屬性可以執行緩存對象user(可以理解為一個map)的key public User getUser(Long userId) { System.out.println("查詢數據庫:userId ->" + userId); return userMap.get(userId); } @Cacheable(value = "nameCache", key = "#name") public User getUserByName(Long userId, String name) { System.out.println("查詢數據庫:userId ->" + userId); return userMap.get(userId); } @Cacheable("nameCache") public User getUserByName(String name) { System.out.println("查詢數據庫:userName : " + name); for (Long k : userMap.keySet()) { if (userMap.get(k).equals(name)) { return userMap.get(k); } } return null; } @CachePut("user") // 與Cacheable區別就是Cacheable先看緩存如果有,直接緩存換回,CachePut則是每次都會調用並且把返回值放到緩存 public User getUser2(Long userId) { System.out.println("查詢數據庫:userId : " + userId); return userMap.get(userId); } @CacheEvict("user") public void removeFromCache(Long userId) { return ; } }
然後我們編寫啟動類:
@SpringBootApplication public class CacheTest implements CommandLineRunner { @Autowired private UserDao userDao; public static void main(String[] args) { new SpringApplication(CacheTest.class).run(args); } @Override public void run(String... args) throws Exception { System.out.println("第一次查詢"); System.out.println(userDao.getUser(1L)); System.out.println("第二次查詢"); System.out.println(userDao.getUser(1L)); userDao.removeFromCache(1L);// 移除緩存 System.out.println("第三次查詢"); userDao.getUser(1L);// 沒有緩存瞭 System.out.println("--------"); // 測試不同的key緩存 userDao.getUserByName("micro1"); userDao.getUserByName(1L, "micro1");// 指定瞭參數name 為key 此次讀取緩存 } }
打印結果:
第一次查詢
查詢數據庫:userId ->1
User@65da01f4
第二次查詢
User@65da01f4
第三次查詢
查詢數據庫:userId ->1
——–
查詢數據庫:userName : micro1
Spring @EnableCaching的工作原理
1、開發人員使用註解@EnableCaching
2、註解@EnableCaching導入CachingConfigurationSelector
3、CachingConfigurationSelector根據註解@EnableCaching 屬性AdviceMode mode決定引入哪些配置類
PROXY
: AutoProxyRegistrar,ProxyCachingConfiguration;ASPECTJ
: AspectJCachingConfiguration;
本文以mode=PROXY為例;
4、CachingConfigurationSelector導入AutoProxyRegistrar會確保容器中存在一個自動代理創建器(APC);
- 用於確保目標bean需要被代理時有可用的代理創建器
5、ProxyCachingConfiguration向容器定義如下基礎設施bean
- 名稱為org.springframework.cache.config.internalCacheAdvisor類型為BeanFactoryCacheOperationSourceAdvisor的bean
- 名稱為cacheOperationSource類型為CacheOperationSource的bean
用於獲取方法調用時最終應用的Spring Cache註解的元數據
- 名稱為cacheInterceptor類型為CacheInterceptor的bean
一個MethodInterceptor,包裹在目標bean外面用於操作Cache的AOP Advice。
6、AutoProxyRegistrar在容器啟動階段對每個bean創建進行處理,如果該bean中有方法應用瞭Spring Cache註解,為其創建相應的代理對象,包裹上面定義的BeanFactoryCacheOperationSourceAdvisor bean;
7、使用瞭Spring Cache註解的bean方法被調用,其實調用首先發生在代理對象上,先到達cacheInterceptor,然後才是目標bean方法的調用;
- cacheInterceptor既處理調用前緩存操作,也處理調用返回時緩存操作
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Java 中的控制反轉(IOC)詳解
- springboot使用redis實現從配置到實戰
- 詳解SpringIOC容器相關知識
- Spring入門到精通之Bean標簽詳解
- SpringBoot+SpringCache實現兩級緩存(Redis+Caffeine)