1、@Cacheable 指定使用緩存
定義個 Controller ,在方法上加上注解?@Cacheable
,配置要使用哪些緩存,比如 myMapCache 表示一級緩存是 Map,myRedisCache 表示二級緩存是 Redis。并配置緩存 key。 key 由 SPEL 表達式組成,Spring 可以動態解析生成緩存 key。
提示:myMapCache、myRedisCache 均為注入 Spring 緩存的名稱
@RestController
public class CachingAnnoController {@Cacheable(cacheNames = {"myMapCache","myRedisCache"},key = "'test_'+#key")@RequestMapping("/cachingAnno")public String cachingAnno(String key,String value){System.out.println("查詢數據庫值 = " + value);return "value";}
}
2、@EnableCaching 開啟緩存功能
用過 @Cacheable 注解的都知道,要開啟緩存功能必須通過 @EnableCaching 注解。代碼如下:
@ComponentScan(value = "com.gwm")
@Configuration
@EnableCaching
public class SpringConfig {}
3、引入具體要使用的緩存—實現 Cache 接口
上述 @EnableCaching、@Cacheable 都是 Spring 提供的基本緩存框架,而具體的緩存需要自己引入。比如現在自己定義兩個緩存:MyMapCache 類 、MyRedisCache 類。然后實現接口方法。
Spring 提供 Cache 作為標準化接口,具體的實現 Spring 不管,你自己去實現。但是 Spring 也是有幾個默認實現的,比如:CaffeineCache、EhCacheCache、ConcurrentMapCache、JCacheCache 等。
public class MyMapCache implements Cache {public static final Map<Object, Object> map = new ConcurrentHashMap<>();private String cacheName;public MyMapCache(String cacheName) {this.cacheName = cacheName;}@Overridepublic String getName() {return cacheName;}@Overridepublic Object getNativeCache() {return null;}@Overridepublic ValueWrapper get(Object key) {System.out.println(">>>>>>我是 MyMapCache 緩存中的 get() 方法");Object o = map.get(key);if (Objects.nonNull(o)) {return new SimpleValueWrapper(o);}return null;}@Overridepublic <T> T get(Object key, Class<T> type) {return (T)map.get(key);}@Overridepublic <T> T get(Object key, Callable<T> valueLoader) {return (T)map.get(key);}@Overridepublic void put(Object key, Object value) {System.out.println(">>>>>>我是 MyMapCache 緩存中的 put() 方法");map.put(key, value);}@Overridepublic void evict(Object key) {map.remove(key);}@Overridepublic void clear() {map.clear();}
}
MyRedisCache 類實現,代碼如下:
public class MyRedisCache implements Cache {private String cacheName;private RedisTemplate<Object,Object> redisTemplate;public MyRedisCache(String cacheName, RedisTemplate<Object, Object> redisTemplate) {this.cacheName = cacheName;this.redisTemplate = redisTemplate;}@Overridepublic String getName() {return cacheName;}@Overridepublic Object getNativeCache() {return this;}@Overridepublic ValueWrapper get(Object key) {System.out.println(">>>>>>我是 MyRedisCache 緩存中的 get() 方法");Object o = redisTemplate.opsForValue().get(key);if (Objects.nonNull(o)) {return new SimpleValueWrapper(o);}return null;}@Overridepublic <T> T get(Object key, Class<T> type) {return null;}@Overridepublic <T> T get(Object key, Callable<T> valueLoader) {return null;}@Overridepublic void put(Object key, Object value) {System.out.println(">>>>>>我是 MyRedisCache 緩存中的 put() 方法");redisTemplate.opsForValue().set(key,value);redisTemplate.expire(key, Duration.ofMillis(3000));}@Overridepublic void evict(Object key) {redisTemplate.delete(key);}@Overridepublic void clear() {redisTemplate.execute((RedisCallback<Object>) conn->{// 清空所有緩存數據,要格外注意這個操作,很危險conn.flushDb();return ">>>>>>flush db success!";});}
}
4、緩存管理類—實現 CacheManager 接口
自定義緩存定義完之后,光擺在這里肯定是不能起作用的,還需要借助 Spring 標準化接口 CacheManager 類來把緩存加入到緩存切面邏輯中。
比如實現 MyMapCache 緩存管理類 MyMapCacheManager,代碼如下:
public class MyMapCacheManager implements CacheManager {@Overridepublic Cache getCache(String name) {return new MyMapCache(name);}@Overridepublic Collection<String> getCacheNames() {return Arrays.asList("myMapCache");}
}
實現 MyRedisCache 緩存管理類 MyRedisCacheManager,代碼如下:
public class MyRedisCacheManager implements CacheManager {@Overridepublic Cache getCache(String name) {return new MyRedisCache(name,redisTemplate);}@Overridepublic Collection<String> getCacheNames() {return Arrays.asList("myRedisCache");}
}
然后在配置入口類中通過 @Bean 引用具體的緩存。代碼如下:
@Configuration
public class MyRedisMainConfig {@Resourceprivate RedisTemplate<Object,Object> redisTemplate;@Beanpublic MyMapCache myMapCache() {MyMapCache myMapCache = new MyMapCache("myMapCache");return myMapCache;}@Beanpublic MyRedisCache myRedisCache() {MyRedisCache myRedisCache = new MyRedisCache("myRedisCache",redisTemplate);return myRedisCache;}@Beanpublic MyRedisCacheManager cacheManager() {MyRedisCacheManager redisCacheManager = new MyRedisCacheManager();return redisCacheManager;}// @Bean public MyMapCacheManager cacheManager() {MyRedisCacheManager redisCacheManager = new MyRedisCacheManager();return redisCacheManager;}}
但是發現使用具體的管理類引入緩存,只能引入對應的緩存。比如 MyRedisCacheManager 管理類就只能引入 MyRedisCache 緩存,不能引入 MyMapCache。所以這是個弊端。如果想要使用雙緩存,那怎么辦呢?
5、雙緩存使用—實現 AbstractCacheManager 抽象類
該抽象類提供 loadCaches() 方法,可以獲取到所有 Cache 接口實現類。所以這里能夠獲取到所有緩存。那么肯定是可以使用雙緩存。比如 MySimpleCacheManager 類實現,代碼如下:
public class MySimpleCacheManager extends AbstractCacheManager implements ApplicationContextAware {private static final List<Cache> list = new ArrayList<>();private ApplicationContext context;/*** 直接實現 AbstractCacheManager 抽象類的鉤子方法,該類已經寫好模版方法* 當執行的時候,如果 MyGuavaCacheManager 管理類 @Bean 的話,就會勾到這個方法邏輯* @return*/@Overrideprotected Collection<? extends Cache> loadCaches() {return list;}@Overridepublic void afterPropertiesSet() {Map<String, Cache> beansOfType = context.getBeansOfType(Cache.class);list.addAll(beansOfType.values());}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException this.context = applicationContext;}
}
然后再配置入口類,代碼如下:
@Configuration
public class MyRedisMainConfig {@Resourceprivate RedisTemplate<Object,Object> redisTemplate;@Beanpublic MyMapCache myMapCache() {MyMapCache myMapCache = new MyMapCache("myMapCache");return myMapCache;}@Beanpublic MyRedisCache myRedisCache() {MyRedisCache myRedisCache = new MyRedisCache("myRedisCache",redisTemplate);return myRedisCache;}@Beanpublic MySimpleCacheManager cacheManager(@Qualifier("myMapCache") MyMapCache myMapCache,@Qualifier("myRedisCache") MyRedisCache myRedisCache,@Qualifier("myGuavaCache") MyGuavaCache myGuavaCache) {SimpleCacheManager simpleCacheManager = new SimpleCacheManager();simpleCacheManager.setCaches(Arrays.asList(myMapCache,myRedisCache,myGuavaCache));return simpleCacheManager;}
}
最終通過注入一個緩存管理類 MySimpleCacheManager 就可以使用到三個緩存。美滋滋。最終要使用,就通過?@Cacheable(cacheNames = {“myMapCache”,“myRedisCache”},key = “‘test_’+#key”)?即可使用 myMapCache、myRedisCache 緩存。如果還需要使用 myGuavaCache,直接往數組中添加即可。
6、 RedisCacheManager 內置類
在使用 Redis 緩存時,Spring 早已對其包裝完成,只需要引入 spring-data-redis 包即可。我們不需要寫 MyRedisCacheManager 類,Spring 早就提供 RedisCacheManager 類管理 Redis 緩存 。所以你只需能夠連接好 Redis 。然后通過 @Bean 將?RedisCacheManager?類注入即可使用 Redis 緩存。