一、Spring Boot 集成 Cache缓存
配置文件pom.xml:
<!-- cache springboot缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
配置文件application.properties:
##cache springboot缓存配置
#simple:使用springboot自带的缓存;适合单体应用或者开发环境使用
#none:关闭缓存
#redis:使用redis作为缓存
spring.cache.type=redis
#基于redis缓存的缓存过期时间为1分钟
spring.cache.redis.time-to-live=60000
二、Cache缓存在Spring Boot中的测试代码
CacheController文件代码如下:
package com.example.shopgoods.controller.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @Author: zp
* @Date: 2019/6/19 14:48
* @Description:
*/
@RestController
@RequestMapping("/cache")
public class CacheController {
@Autowired
private CacheService cacheService;
@PostMapping("/test_1")
public String cacheTest1(@RequestParam(value = "id") int id){
String str = cacheService.getCache(id);
return str;
}
@PostMapping("/test_2")
public String cacheTest2(@RequestParam(value = "id") int id){
String str = cacheService.getCacheName(id);
return str;
}
}
CacheService文件代码如下:
package com.example.shopgoods.controller.cache;
/**
* @Author: zp
* @Date: 2019/6/19 14:50
* @Description:
*/
public interface CacheService {
String getCache(int id);
String getCacheName(int id);
}
CacheServiceImpl文件代码如下:
package com.example.shopgoods.controller.cache.Impl;
import com.example.shopgoods.controller.cache.CacheService;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* @Author: zp
* @Date: 2019/6/19 14:50
* @Description:
*/
@Service
//该注解作用于类上,为此类里的方法的缓存注解提供默认值
@CacheConfig(cacheNames = "cache")
public class CacheServiceImpl implements CacheService {
@Override
//id小于5的都需要缓存
@Cacheable(cacheNames = "getCache", condition = "#id < 5")
public String getCache(int id) {
String str = "";
if (id == 1) {
str = "C_1 ---->" + id;
} else if (id == 2) {
str = "C_2 ---->" + id;
} else {
str = "cache ---->" + id;
}
return str;
}
@Override
@Cacheable()
public String getCacheName(int id) {
return String.valueOf(id);
}
}
CacheConfig文件代码如下:(此配置介绍了实现Redis两级缓存的原理)
package com.example.shopgoods.controller.cache.config;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
/**
* 支持一二级缓存,使得性能到达极致,
* @author xiandafu
*
*/
//@Configuration
public class CacheConfig {
// 定义一个redis 的频道,默认叫cache,用于pub/sub
@Value("${springext.cache.redis.topic:cache}")
String topicName;
@Bean
public TwoLevelCacheManager cacheManager(StringRedisTemplate redisTemplate) {
//RedisCache需要一个RedisCacheWriter来实现读写Redis
RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(redisTemplate.getConnectionFactory());
/*SerializationPair用于Java和Redis之间的序列化和反序列化*/
SerializationPair pair = SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(this.getClass().getClassLoader()));
/*构造一个RedisCache的配置。*/
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
/*创建CacheManager,并返回给Spring容器*/
TwoLevelCacheManager cacheManager = new TwoLevelCacheManager(redisTemplate,writer,config);
return cacheManager;
}
// Redis message,参考Redis一章
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic(topicName));
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(final TwoLevelCacheManager cacheManager) {
return new MessageListenerAdapter(new MessageListener() {
public void onMessage(Message message, byte[] pattern) {
byte[] bs = message.getChannel();
try {
// Sub 一个消息,通知缓存管理器
String type = new String(bs, "UTF-8");
String cacheName = new String(message.getBody(),"UTF-8");
cacheManager.receiver(cacheName);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
// 不可能出错,忽略
}
}
});
}
class TwoLevelCacheManager extends RedisCacheManager {
RedisTemplate redisTemplate;
public TwoLevelCacheManager(RedisTemplate redisTemplate,RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter,defaultCacheConfiguration);
this.redisTemplate = redisTemplate;
}
//使用RedisAndLocalCache代替Spring Boot自带的RedisCache
@Override
protected Cache decorateCache(Cache cache) {
return new RedisAndLocalCache(this, (RedisCache) cache);
}
//通过其他分布式节点,缓存改变
public void publishMessage(String cacheName) {
this.redisTemplate.convertAndSend(topicName, cacheName);
}
// 接受一个消息清空本地缓存
public void receiver(String name) {
RedisAndLocalCache cache = ((RedisAndLocalCache) this.getCache(name));
if(cache!=null){
cache.clearLocal();
}
}
}
class RedisAndLocalCache implements Cache {
// 本地缓存提供
ConcurrentHashMap<Object, Object> local = new ConcurrentHashMap<Object, Object>();
RedisCache redisCache;
TwoLevelCacheManager cacheManager;
public RedisAndLocalCache(TwoLevelCacheManager cacheManager, RedisCache redisCache) {
this.redisCache = redisCache;
this.cacheManager = cacheManager;
}
@Override
public String getName() {
return redisCache.getName();
}
@Override
public Object getNativeCache() {
return redisCache.getNativeCache();
}
@Override
public ValueWrapper get(Object key) {
//一级缓存先取
ValueWrapper wrapper = (ValueWrapper) local.get(key);
if (wrapper != null) {
return wrapper;
} else {
// 二级缓存取
wrapper = redisCache.get(key);
if (wrapper != null) {
local.put(key, wrapper);
}
return wrapper;
}
}
@Override
public <T> T get(Object key, Class<T> type) {
return redisCache.get(key, type);
}
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
return redisCache.get(key, valueLoader);
}
@Override
public void put(Object key, Object value) {
System.out.println(value.getClass().getClassLoader());
redisCache.put(key, value);
//通知其他节点缓存更更新
clearOtherJVM();
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
ValueWrapper v = redisCache.putIfAbsent(key, value);
clearOtherJVM();
return v;
}
@Override
public void evict(Object key) {
redisCache.evict(key);
//通知其他节点缓存更更新
clearOtherJVM();
}
@Override
public void clear() {
redisCache.clear();
}
/**
* 提供给CacheManager清空一级缓存
*/
public void clearLocal() {
this.local.clear();
}
/**
* 通知其他节点缓存更更新
*/
protected void clearOtherJVM() {
cacheManager.publishMessage(redisCache.getName());
}
}
}