0.导入pom文件
父级模块的pom文件添加
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
子级模块的pom文件添加
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.注册RedisTemplate客户端
1.1)配置文件
spring.redis.pool.max-idle=200
spring.redis.pool.min-idle=10
spring.redis.pool.max-active=80
spring.redis.pool.max-wait=-1
spring.redis.database=0
#server host
spring.redis.host=127.0.0.1
#server password
spring.redis.password=
#connection port
spring.redis.port=6379
1.2) 把redisTemplate客户端交给spring容器管理
package org.github.roger.cache.config;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.github.roger.serializer.FastJsonRedisSerializer;
import org.github.roger.serializer.KryoRedisSerializer;
import org.github.roger.serializer.StringRedisSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
@PropertySource({"classpath:application.properties"})
public class RedisConfig {
@Value("${spring.redis.database:0}")
private int database;
@Value("${spring.redis.host:127.0.0.1}")
private String host;
@Value("${spring.redis.password:}")
private String password;
@Value("${spring.redis.port:6379}")
private int port;
@Value("${spring.redis.pool.max-idle:200}")
private int maxIdle;
@Value("${spring.redis.pool.min-idle:10}")
private int minIdle;
@Value("${spring.redis.pool.max-active:80}")
private int maxActive;
@Value("${spring.redis.pool.max-wait:-1}")
private int maxWait;
// @Bean
// public JedisConnectionFactory redisConnectionFactory() {
// JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// jedisPoolConfig.setMinIdle(minIdle);
// jedisPoolConfig.setMaxIdle(maxIdle);
// jedisPoolConfig.setMaxTotal(maxActive);
// jedisPoolConfig.setMaxWaitMillis(maxWait);
//
// JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig);
// jedisConnectionFactory.setDatabase(database);
// jedisConnectionFactory.setHostName(host);
// jedisConnectionFactory.setPassword(password);
// jedisConnectionFactory.setPort(port);
// jedisConnectionFactory.setUsePool(true);
// return jedisConnectionFactory;
// }
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
ClientResources clientResources = DefaultClientResources.create();
LettuceClientConfigurationBuilder builder = LettuceClientConfiguration.builder();
builder.clientResources(clientResources);
LettuceClientConfiguration configuration = builder.build();
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
config.setPassword(RedisPassword.of(password));
config.setDatabase(database);
return new LettuceConnectionFactory(config, configuration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<Object>(Object.class, "com.xxx.");
KryoRedisSerializer<Object> kryoRedisSerializer = new KryoRedisSerializer<Object>(Object.class);
// 设置值(value)的序列化采用FastJsonRedisSerializer。
redisTemplate.setValueSerializer(kryoRedisSerializer);
redisTemplate.setHashValueSerializer(kryoRedisSerializer);
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
2.利用1注册的RedisTemplate客户端构造多级缓存管理器,并交给Spring容器管理
package org.github.roger.cache.config;
import org.github.roger.MultiLayeringCacheManager;
import org.github.roger.manager.ICacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
@Import({RedisConfig.class})
public class ICacheManagerConfig {
@Bean
public ICacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
MultiLayeringCacheManager layeringCacheManager = new MultiLayeringCacheManager(redisTemplate);
return layeringCacheManager;
}
}
3.创建多级缓存测试类
3.1) 测试 org.github.roger.manager.ICacheManager#getCache(java.lang.String, org.github.roger.settings.MultiLayeringCacheSetting)
该方法的功能: 根据缓存名称获取缓存API操作接口,获取到直接返回,如果获取不到,就创建一个,使用
org.github.roger.manager.AbstractCacheManager#getMissingCache方法指定一级缓存和二级缓存分别是什么。
package org.github.roger.cache.core;
import org.github.roger.cache.ICache;
import org.github.roger.cache.config.ICacheManagerConfig;
import org.github.roger.enumeration.ExpireMode;
import org.github.roger.manager.ICacheManager;
import org.github.roger.settings.FirstCacheSetting;
import org.github.roger.settings.MultiLayeringCacheSetting;
import org.github.roger.settings.SecondaryCacheSetting;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
// SpringJUnit4ClassRunner再Junit环境下提供Spring TestContext Framework的功能。
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration用来加载配置ApplicationContext,其中classes用来加载配置类
@ContextConfiguration(classes = {ICacheManagerConfig.class})
public class CacheCoreTest {
private Logger logger = LoggerFactory.getLogger(CacheCoreTest.class);
@Autowired
private ICacheManager cacheManager;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private MultiLayeringCacheSetting layeringCacheSetting1;
private MultiLayeringCacheSetting layeringCacheSetting2;
private MultiLayeringCacheSetting layeringCacheSetting4;
private MultiLayeringCacheSetting layeringCacheSetting5;
@Before
public void before() {
// 测试 CacheManager getCache方法
FirstCacheSetting firstCacheSetting1 = new FirstCacheSetting(10, 1000, 4, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting1 = new SecondaryCacheSetting(10, 4, TimeUnit.SECONDS, true, true, true,1);
layeringCacheSetting1 = new MultiLayeringCacheSetting(firstCacheSetting1, secondaryCacheSetting1);
// 二级缓存可以缓存null,时间倍率是1
FirstCacheSetting firstCacheSetting2 = new FirstCacheSetting(10, 1000, 5, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting2 = new SecondaryCacheSetting(3000, 14, TimeUnit.SECONDS, true, true,true, 1);
layeringCacheSetting2 = new MultiLayeringCacheSetting(firstCacheSetting2, secondaryCacheSetting2);
// 二级缓存可以缓存null,时间倍率是10
FirstCacheSetting firstCacheSetting4 = new FirstCacheSetting(10, 1000, 5, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting4 = new SecondaryCacheSetting(100, 70, TimeUnit.SECONDS, true, true, true,10);
layeringCacheSetting4 = new MultiLayeringCacheSetting(firstCacheSetting4, secondaryCacheSetting4);
// 二级缓存不可以缓存null
FirstCacheSetting firstCacheSetting5 = new FirstCacheSetting(10, 1000, 5, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting5 = new SecondaryCacheSetting(10, 7, TimeUnit.SECONDS, true, false,true, 1);
layeringCacheSetting5 = new MultiLayeringCacheSetting(firstCacheSetting5, secondaryCacheSetting5);
}
@Test
public void testGetCache(){
String cacheName = "cache:name";
ICache cache1 = cacheManager.getCache(cacheName, layeringCacheSetting1);
ICache cache2 = cacheManager.getCache(cacheName, layeringCacheSetting1);
Assert.assertEquals(cache1, cache2);
ICache cache3 = cacheManager.getCache(cacheName, layeringCacheSetting2);
Collection<ICache> caches = cacheManager.getCache(cacheName);
Assert.assertTrue(caches.size() == 2);
Assert.assertNotEquals(cache1, cache3);
}
}
3.2)测试缓存的过期时间
package org.github.roger.cache.core;
import lombok.extern.slf4j.Slf4j;
import org.github.roger.MultiLayeringCache;
import org.github.roger.cache.ICache;
import org.github.roger.cache.config.ICacheManagerConfig;
import org.github.roger.cache.redis.RedisCache;
import org.github.roger.enumeration.ExpireMode;
import org.github.roger.manager.ICacheManager;
import org.github.roger.settings.FirstCacheSetting;
import org.github.roger.settings.MultiLayeringCacheSetting;
import org.github.roger.settings.SecondaryCacheSetting;
import org.github.roger.utils.RedisCacheKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
// SpringJUnit4ClassRunner再Junit环境下提供Spring TestContext Framework的功能。
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration用来加载配置ApplicationContext,其中classes用来加载配置类
@ContextConfiguration(classes = {ICacheManagerConfig.class})
@Slf4j
public class CacheCoreTest {
@Autowired
private ICacheManager cacheManager;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private MultiLayeringCacheSetting layeringCacheSetting1;
private MultiLayeringCacheSetting layeringCacheSetting2;
private MultiLayeringCacheSetting layeringCacheSetting4;
private MultiLayeringCacheSetting layeringCacheSetting5;
@Before
public void before() {
// 测试 CacheManager getCache方法
FirstCacheSetting firstCacheSetting1 = new FirstCacheSetting(10, 1000, 4, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting1 = new SecondaryCacheSetting(10, 4, TimeUnit.SECONDS, true, true, true, 1);
layeringCacheSetting1 = new MultiLayeringCacheSetting(firstCacheSetting1, secondaryCacheSetting1);
// 二级缓存可以缓存null,时间倍率是1
FirstCacheSetting firstCacheSetting2 = new FirstCacheSetting(10, 1000, 5, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting2 = new SecondaryCacheSetting(3000, 14, TimeUnit.SECONDS, true, true, true, 1);
layeringCacheSetting2 = new MultiLayeringCacheSetting(firstCacheSetting2, secondaryCacheSetting2);
// 二级缓存可以缓存null,时间倍率是10
FirstCacheSetting firstCacheSetting4 = new FirstCacheSetting(10, 1000, 5, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting4 = new SecondaryCacheSetting(100, 70, TimeUnit.SECONDS, true, true, true, 10);
layeringCacheSetting4 = new MultiLayeringCacheSetting(firstCacheSetting4, secondaryCacheSetting4);
// 二级缓存不可以缓存null
FirstCacheSetting firstCacheSetting5 = new FirstCacheSetting(10, 1000, 5, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting5 = new SecondaryCacheSetting(10, 7, TimeUnit.SECONDS, true, false, true, 1);
layeringCacheSetting5 = new MultiLayeringCacheSetting(firstCacheSetting5, secondaryCacheSetting5);
}
@Test
public void testGetCache() {
String cacheName = "cache:name";
ICache cache1 = cacheManager.getCache(cacheName, layeringCacheSetting1);
ICache cache2 = cacheManager.getCache(cacheName, layeringCacheSetting1);
Assert.assertEquals(cache1, cache2);
ICache cache3 = cacheManager.getCache(cacheName, layeringCacheSetting2);
Collection<ICache> caches = cacheManager.getCache(cacheName);
Assert.assertTrue(caches.size() == 2);
Assert.assertNotEquals(cache1, cache3);
}
@Test
public void testCacheExpiration() {
//一级缓存的有效时间是在写入之后,4秒
String cacheName = "cache:name";
String cacheKey1 = "cache:key1";
MultiLayeringCache cache1 = (MultiLayeringCache) cacheManager.getCache(cacheName, layeringCacheSetting1);
cache1.get(cacheKey1, () -> initCache(String.class));
// 测试一级缓存值及过期时间
String str1 = cache1.getFirstCache().get(cacheKey1, String.class);
String st2 = cache1.getFirstCache().get(cacheKey1, () -> initCache(String.class));
log.debug("========================:{}", str1);
Assert.assertTrue(str1.equals(st2));
Assert.assertTrue(str1.equals(initCache(String.class)));
sleep(5);
Assert.assertNull(cache1.getFirstCache().get(cacheKey1, String.class));
// 看日志是不是走了二级缓存
cache1.get(cacheKey1, () -> initCache(String.class));
log.debug("***********************");
log.debug("***********************");
log.debug("***********************");
//二级缓存的有效时间是10秒,在失效前 4秒强制刷新缓存
str1 = cache1.getSecondCache().get(cacheKey1, String.class);
st2 = cache1.getSecondCache().get(cacheKey1, () -> initCache(String.class));
Assert.assertTrue(st2.equals(str1));
Assert.assertTrue(str1.equals(initCache(String.class)));
sleep(5);
// 看日志是不是走了自动刷新
RedisCacheKey redisCacheKey = ((RedisCache) cache1.getSecondCache()).getRedisCacheKey(cacheKey1);
cache1.get(cacheKey1, () -> initCache(String.class));
sleep(6);
Long ttl = redisTemplate.getExpire(redisCacheKey.getKey());
log.debug("========================ttl 1:{}", ttl);
Assert.assertNotNull(cache1.getSecondCache().get(cacheKey1));
sleep(5);
ttl = redisTemplate.getExpire(redisCacheKey.getKey());
log.debug("========================ttl 2:{}", ttl);
Assert.assertNull(cache1.getSecondCache().get(cacheKey1));
}
private <T> T initCache(Class<T> t) {
log.debug("加载缓存");
return (T) "test";
}
private void sleep(int time) {
try {
Thread.sleep(time * 1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
}
}
3.测试删除某个缓存key
13:54:50.799 [main] INFO org.github.roger.cache.redis.RedisCache
- 清除redis缓存 key= cache:name:cache:key2
13:55:16.762 [main] DEBUG org.github.roger.listener.RedisPublisher
- redis消息发布者向频道【cache:name】发布了【RedisPubSubMessage(cacheName=cache:name, key=cache:key2, messageType=EVICT)】消息
13:57:06.707 [RedisMessageListenerContainer-3] DEBUG org.github.roger.listener.RedisMessageListener
- redis消息订阅者接收到频道【cache:name】发布的消息。消息内容:{"cacheName":"cache:name","key":"cache:key2","messageType":"EVICT"}