Spring's @Cacheable cache usage tutorial

1. @Cacheable specifies the use of cache

Define a Controller, add annotations to the method @Cacheable, and configure which caches to use. For example, myMapCache means that the first-level cache is Map, and myRedisCache means that the second-level cache is Redis. And configure the cache key. The key consists of a SPEL expression, and Spring can dynamically parse it to generate a cache key.

Tip: myMapCache and myRedisCache are the names injected into Spring cache.

@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 turns on the caching function

Anyone who has used the @Cacheable annotation knows that to enable the caching function, the @EnableCaching annotation must be passed. code show as below:

@ComponentScan(value = "com.gwm")
@Configuration
@EnableCaching
public class SpringConfig {
    
    

}

3. Introduce the specific cache to be used—implement the Cache interface

The above @EnableCaching and @Cacheable are the basic caching framework provided by Spring, and the specific cache needs to be introduced by yourself. For example, now you define two caches: MyMapCache class and MyRedisCache class. Then implement the interface methods.

Spring provides Cache as a standardized interface. Spring does not care about the specific implementation, you can implement it yourself. But Spring also has several default implementations, such as: CaffeineCache, EhCacheCache, ConcurrentMapCache, JCacheCache, etc.

public class MyMapCache implements Cache {
    
    

	public static final Map<Object, Object> map = new ConcurrentHashMap<>();

	private String cacheName;

	public MyMapCache(String cacheName) {
    
    
		this.cacheName = cacheName;
	}

	@Override
	public String getName() {
    
    
		return cacheName;
	}

	@Override
	public Object getNativeCache() {
    
    
		return null;
	}

	@Override
	public ValueWrapper get(Object key) {
    
    
		System.out.println(">>>>>>我是 MyMapCache 缓存中的 get() 方法");
		Object o = map.get(key);
		if (Objects.nonNull(o)) {
    
    
			return new SimpleValueWrapper(o);
		}
		return null;
	}

	@Override
	public <T> T get(Object key, Class<T> type) {
    
    

		return (T)map.get(key);
	}

	@Override
	public <T> T get(Object key, Callable<T> valueLoader) {
    
    
		return (T)map.get(key);
	}

	@Override
	public void put(Object key, Object value) {
    
    
		System.out.println(">>>>>>我是 MyMapCache 缓存中的 put() 方法");
		map.put(key, value);
	}

	@Override
	public void evict(Object key) {
    
    
		map.remove(key);
	}

	@Override
	public void clear() {
    
    
		map.clear();
	}
}

MyRedisCache class implementation, the code is as follows:

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;
	}

	@Override
	public String getName() {
    
    
		return cacheName;
	}

	@Override
	public Object getNativeCache() {
    
    
		return this;
	}

	@Override
	public 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;
	}

	@Override
	public <T> T get(Object key, Class<T> type) {
    
    
		return null;
	}

	@Override
	public <T> T get(Object key, Callable<T> valueLoader) {
    
    
		return null;
	}

	@Override
	public void put(Object key, Object value) {
    
    
		System.out.println(">>>>>>我是 MyRedisCache 缓存中的 put() 方法");
		redisTemplate.opsForValue().set(key,value);
		redisTemplate.expire(key, Duration.ofMillis(3000));
	}

	@Override
	public void evict(Object key) {
    
    
		redisTemplate.delete(key);
	}

	@Override
	public void clear() {
    
    
		redisTemplate.execute((RedisCallback<Object>) conn->{
    
    

			// 清空所有缓存数据,要格外注意这个操作,很危险
			conn.flushDb();

			return ">>>>>>flush db success!";
		});
	}
}

4. Cache management class—implement the CacheManager interface

After the custom cache is defined, it will definitely not work just here. You also need to use the Spring standardized interface CacheManager class to add the cache to the cache aspect logic.

For example, to implement the MyMapCache cache management class MyMapCacheManager, the code is as follows:

public class MyMapCacheManager implements CacheManager {
    
    

	@Override
	public Cache getCache(String name) {
    
    
		return new MyMapCache(name);
	}

	@Override
	public Collection<String> getCacheNames() {
    
    
		return Arrays.asList("myMapCache");
	}
}

Implement the MyRedisCache cache management class MyRedisCacheManager, the code is as follows:

public class MyRedisCacheManager implements CacheManager {
    
    

	@Override
	public Cache getCache(String name) {
    
    
		return new MyRedisCache(name,redisTemplate);
	}

	@Override
	public Collection<String> getCacheNames() {
    
    
		return Arrays.asList("myRedisCache");
	}
}

Then reference the specific cache through @Bean in the configuration entry class. code show as below:

@Configuration
public class MyRedisMainConfig {
    
    

	@Resource
	private RedisTemplate<Object,Object> redisTemplate;

	@Bean
	public MyMapCache myMapCache() {
    
    
		MyMapCache myMapCache = new MyMapCache("myMapCache");
		return myMapCache;
	}
	
	@Bean
	public MyRedisCache myRedisCache() {
    
    
		MyRedisCache myRedisCache = new MyRedisCache("myRedisCache",redisTemplate);

		return myRedisCache;
	}
	
	@Bean
	public MyRedisCacheManager cacheManager() {
    
    
		MyRedisCacheManager redisCacheManager = new MyRedisCacheManager();
		return redisCacheManager;
	}
	
	// @Bean 
	public MyMapCacheManager cacheManager() {
    
    
		MyRedisCacheManager redisCacheManager = new MyRedisCacheManager();
		return redisCacheManager;
	}
	
}

However, it was found that using a specific management class to introduce cache can only introduce the corresponding cache. For example, the MyRedisCacheManager management class can only introduce the MyRedisCache cache, but not MyMapCache. So this is a drawback. What should I do if I want to use double buffering?

5. Use of double cache—implement the AbstractCacheManager abstract class

This abstract class provides the loadCaches() method, which can obtain all Cache interface implementation classes. So all caches can be obtained here. Then you can definitely use double buffering. For example, the MySimpleCacheManager class is implemented with the following code:

public class MySimpleCacheManager extends AbstractCacheManager implements ApplicationContextAware {
    
    

	private static final List<Cache> list = new ArrayList<>();

	private ApplicationContext context;
	/**
	 * 直接实现 AbstractCacheManager 抽象类的钩子方法,该类已经写好模版方法
	 * 当执行的时候,如果 MyGuavaCacheManager 管理类 @Bean 的话,就会勾到这个方法逻辑
	 * @return
	 */
	@Override
	protected Collection<? extends Cache> loadCaches() {
    
    
		return list;
	}

	@Override
	public void afterPropertiesSet() {
    
    
		Map<String, Cache> beansOfType = context.getBeansOfType(Cache.class);
		list.addAll(beansOfType.values());
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
		this.context = applicationContext;
	}
}

Then configure the entry class, the code is as follows:

@Configuration
public class MyRedisMainConfig {
    
    

	@Resource
	private RedisTemplate<Object,Object> redisTemplate;

	@Bean
	public MyMapCache myMapCache() {
    
    
		MyMapCache myMapCache = new MyMapCache("myMapCache");
		return myMapCache;
	}
	
	@Bean
	public MyRedisCache myRedisCache() {
    
    
		MyRedisCache myRedisCache = new MyRedisCache("myRedisCache",redisTemplate);
		return myRedisCache;
	}
	
	@Bean
	public 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;
	}
}

Finally, by injecting a cache management class MySimpleCacheManager, three caches can be used. Beautiful. To finally use it, pass@Cacheable(cacheNames = {“myMapCache”,“myRedisCache”},key = “‘test_’+#key”)You can use myMapCache and myRedisCache cache. If you still need to use myGuavaCache, just add it directly to the array.

6. RedisCacheManager built-in class

When using Redis cache, Spring has already packaged it, and you only need to introduce the spring-data-redis package. We don't need to write the MyRedisCacheManager class. Spring has already provided the RedisCacheManager class to manage the Redis cache. So you just need to be able to connect to Redis. Then inject the RedisCacheManager class through @Bean to use the Redis cache.

Guess you like

Origin blog.csdn.net/qq_35971258/article/details/128724965