環境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPA
Spring 框架從 3.1 開始,對 Spring 應用程序提供了透明式添加緩存的支持。和事務支持一樣,抽象緩存允許一致地使用各種緩存解決方案,并對代碼的影響最小。從 Spring4.1 版本開始,緩存抽象支持了 JSR-107 注釋和更多自定義選項,從而得到了顯著的改進。
方式1:直接使用spring的注解來實現緩存
spring提供了如下注解:
@Cacheable 觸發緩存機制
@CacheEvict 觸發緩存回收
@CachePut 更新緩存,而不會影響方法的執行
@Caching 組合多個緩存操作到一個方法
@CacheConfig 類級別共享系誒常見的緩存相關配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId></dependency>
首先在Service對應的方法是添加注解:
@Servicepublic class StorageService { @Resource private StorageRepository sr ; @Cacheable(value = {"cache_storage"}, keyGenerator = "storageKey") public Storage getStorage(Long id) { return sr.findById(id).get() ; } }
// 這里的keyGenerator是你自定義Key生成的Bean名稱@Component("storageKey")public class StorageKeyGenerator implements KeyGenerator { private static final String KEY_PREFIX = "storage_" ; @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder() ; for (Object param : params) { sb.append(param) ; } return KEY_PREFIX + sb.toString() ; }}
web接口:
@RestController@RequestMapping("/storages")public class StorageController { @Resource private StorageService storageService ; @GetMapping("/{id}") public Object get(@PathVariable("id") Long id) { return storageService.getStorage(id) ; }}
測試:
第一次訪問接口,查看控制臺輸出了sql語句:
圖片
再次訪問接口,發現控制臺沒有再輸出任何sql,說明我們的緩存生效了(這里你也可以把這里的注解注釋了來看效果)。關于這里的更新緩存,刪除緩存就不演示了。接下來完整的演示下JSR107規范中的注解演示:
注意在這些注釋中我們是可以使用SpEL表達式的:
圖片
方式2:使用JSR107和Ehcache
先來看看Spring與JSR107注解的對照表:
圖片
pom.xml中加入依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId></dependency><dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId></dependency>
Service類:
@Servicepublic class StorageService { @Resource private StorageRepository sr ; // 這里的 @CacheValue 說明是要緩存的參數值。 @Transactional @CachePut(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class) public Storage save(@CacheValue Storage storage) { return sr.saveAndFlush(storage) ; } @CacheResult(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class) public Storage getStorage(Long id) { return sr.findById(id).get() ; } @Transactional @CacheRemove(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class) public void removeStorage(Long id) { sr.deleteById(id) ; } @Transactional @CachePut(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class) public Storage updateStorage(@CacheValue Storage storage) { return sr.saveAndFlush(storage) ; }}// 注意這里的cacheKeyGenerator 必須全部用同一個,// 跟蹤了下源碼是用的對應的類名key來查找對應的緩存的;一開始我沒有用同一個始終不正確。。// 看下圖跟蹤的代碼:
圖片
這里必須要一樣哦cacheKeyGenerator
緩存Key:JCacheKeyGenerator.java
public class JCacheKeyGenerator implements CacheKeyGenerator { private static final String KEY_PREFIX = "storage_" ; @Override public GeneratedCacheKey generateCacheKey( CacheKeyInvocationContext<? extends Annotation> cacheKeyInvocationContext) { CacheInvocationParameter[] params = cacheKeyInvocationContext.getAllParameters() ; StringBuilder sb = new StringBuilder() ; for (CacheInvocationParameter param : params) { if (param.getValue() instanceof Storage) { Storage s = (Storage) param.getValue() ; sb.append(s.getId()) ; } else { sb.append((Long)param.getValue()) ; } } return new StorageGeneratedCacheKey(KEY_PREFIX + sb.toString()) ; } private static class StorageGeneratedCacheKey implements GeneratedCacheKey { private static final long serialVersionUID = 1L; private String key ; public StorageGeneratedCacheKey(String key) { this.key = key ; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StorageGeneratedCacheKey other = (StorageGeneratedCacheKey) obj; if (key == null) { if (other.key != null) return false; } else if (!key.equals(other.key)) return false; return true; } }}
application.yml配置:
spring: cache: cacheNames: - cache_storage ehcache: config: classpath:ehcache.xml
ehcache.xml
<?xml versinotallow="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="java.io.tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU" /> <cache name="cache_storage" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU" /> </ehcache>
測試增刪改:
先添加個數據:
圖片
圖片
成功添加ID為4的信息,Service中的save方法中我們添加了@CachePut注解,接下來我們查詢ID為4的信息,看看控制臺是否會生成SQL語句。
圖片
圖片
控制臺沒有增加任何的SQL語句,說明save方法加的@CachePut生效了。
接著做刪除操作:
圖片
圖片
ID為4的刪除了,接下來再做查詢看看:
圖片
這說明刪除了數據后,緩存也做了刪除。這里生成了查詢語句。
本文鏈接:http://www.tebozhan.com/showinfo-26-89-0.htmlSpringBoot中使用Cache提升接口性能詳解
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: K8S | Service服務發現
下一篇: 一年經驗在二線城市面試后端的經驗分享