Redis是一个键值对内存数据库,也支持持久化到磁盘
brew install redis
在macOS中安装Redisredis-server
在前台启动Redis,监听6379端口添加依赖spring-boot-starter-data-redis
即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
package bj.redis;
import io.vavr.control.Try;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* Created by BaiJiFeiLong@gmail.com at 2018/12/13 上午9:51
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class RedisApp implements ApplicationListener<ApplicationReadyEvent> {
@Resource
private StringRedisTemplate stringRedisTemplate;
public static void main(String[] args) {
SpringApplication.run(RedisApp.class, args);
}
@Override
public void onApplicationEvent(@NotNull ApplicationReadyEvent event) {
Try.run(this::_onReady).getOrElseThrow((Function<Throwable, RuntimeException>) RuntimeException::new);
}
private void _onReady() throws InterruptedException {
stringRedisTemplate.opsForValue().set("GMail", "Google", 1, TimeUnit.SECONDS);
System.out.println("[After cached] GMail: " + stringRedisTemplate.opsForValue().get("GMail"));
Thread.sleep(1000);
System.out.println("[After timeout] GMail: " + stringRedisTemplate.opsForValue().get("GMail"));
}
}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.0.RELEASE)
2018-12-13 10:15:58.921 INFO 44761 --- [ main] bj.redis.RedisApp : Starting RedisApp on MacBook-Air-2.local with PID 44761 (/Users/yuchao/temp/java/hellomaven/target/classes started by yuchao in /Users/yuchao/temp/java/hellomaven)
2018-12-13 10:15:58.936 INFO 44761 --- [ main] bj.redis.RedisApp : No active profile set, falling back to default profiles: default
2018-12-13 10:16:00.818 INFO 44761 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2018-12-13 10:16:00.821 INFO 44761 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2018-12-13 10:16:00.859 INFO 44761 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17ms. Found 0 repository interfaces.
2018-12-13 10:16:02.564 INFO 44761 --- [ main] bj.redis.RedisApp : Started RedisApp in 4.561 seconds (JVM running for 6.042)
2018-12-13 10:16:02.692 INFO 44761 --- [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library
2018-12-13 10:16:02.809 INFO 44761 --- [ main] io.lettuce.core.KqueueProvider : Starting with kqueue library
[After cached] GMail: Google
[After timeout] GMail: null
package bj.redis;
import io.vavr.collection.HashMap;
import io.vavr.control.Try;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* Created by BaiJiFeiLong@Gmail.com at 2018/12/13 上午9:51
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableCaching
public class RedisApp implements ApplicationListener<ApplicationReadyEvent> {
public static void main(String[] args) {
SpringApplication.run(RedisApp.class, args);
}
@Override
public void onApplicationEvent(@NotNull ApplicationReadyEvent event) {
Try.run(this::_onReady).getOrElseThrow((Function<Throwable, RuntimeException>) RuntimeException::new);
}
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private Inner inner;
private void _onReady() throws InterruptedException {
stringRedisTemplate.opsForValue().set("a", "b");
System.out.println("TTL of a: " + stringRedisTemplate.getExpire("a", TimeUnit.MILLISECONDS));
System.out.println("[Before cache] Owner of Gmail: " + inner.owner("Gmail"));
System.out.println("[After cache] Owner of Gmail: " + inner.owner("Gmail"));
System.out.println("Expire millis: " + stringRedisTemplate.getExpire("alpha::owner::Gmail", TimeUnit.MILLISECONDS));
System.out.println("Keys: " + stringRedisTemplate.keys("*"));
Thread.sleep(100);
System.out.println("[After expire] Owner of Gmail: " + inner.owner("Gmail"));
System.out.println("Raw: " + stringRedisTemplate.opsForValue().get("alpha::owner::Gmail"));
}
@Component
static class Inner {
@Cacheable("alpha::owner")
public String owner(String product) {
System.out.println("Fetching...");
return HashMap.of("Gmail", "Google").getOrElse(product, null);
}
}
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofMillis(100));
}
}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.0.RELEASE)
2018-12-13 11:48:16.788 INFO 45720 --- [ main] bj.redis.RedisApp : Starting RedisApp on MacBook-Air-2.local with PID 45720 (/Users/yuchao/temp/java/hellomaven/target/classes started by yuchao in /Users/yuchao/temp/java/hellomaven)
2018-12-13 11:48:16.810 INFO 45720 --- [ main] bj.redis.RedisApp : No active profile set, falling back to default profiles: default
2018-12-13 11:48:18.079 INFO 45720 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2018-12-13 11:48:18.082 INFO 45720 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2018-12-13 11:48:18.117 INFO 45720 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 16ms. Found 0 repository interfaces.
2018-12-13 11:48:19.243 INFO 45720 --- [ main] bj.redis.RedisApp : Started RedisApp in 3.305 seconds (JVM running for 4.684)
2018-12-13 11:48:19.484 INFO 45720 --- [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library
2018-12-13 11:48:19.512 INFO 45720 --- [ main] io.lettuce.core.KqueueProvider : Starting with kqueue library
TTL of a: -1
Fetching...
[Before cache] Owner of Gmail: Google
[After cache] Owner of Gmail: Google
Expire millis: 54
Keys: [a, alpha::owner::Gmail]
Fetching...
[After expire] Owner of Gmail: Google
Raw: "Google"
org.springframework.data.redis.cache.RedisCacheConfiguration
。自定义RedisTemplate
不管用RedisCacheConfiguration
的配置不影响RedisTemplate
和StringRedisTemplate
的注入serializeValuesWith
可以配置缓存值的序列化策略entryTtl
可以配置Redis的全局缓存超时stringRedisTemplate.keys("*")
可以获取所有缓存键。不能使用redisTemplate
org.springframework.data.redis.core.RedisTemplate#getExpire(K, java.util.concurrent.TimeUnit)
获取缓存键超时时间org.springframework.data.redis.cache.RedisCacheConfiguration#prefixKeysWith
进行自定义RedisCacheManager
(org.springframework.data.redis.cache.RedisCacheManager
)去掉了TTL相关逻辑。要想通过@Cacheable
注解实现自定义缓存TTL,非常困难,应该得自定义org.springframework.data.redis.cache.RedisCacheWriter
package bj.redis;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.awt.*;
/**
* Created by BaiJiFeiLong@gmail.com at 2018/12/26 下午8:04
*/
public class RedisApp2 {
@SuppressWarnings("ConstantConditions")
public static void main(String[] args) {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.INFO);
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(new LettuceConnectionFactory() {{
afterPropertiesSet();
}});
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
redisTemplate.opsForValue().set("point", new Point(0, 1));
System.out.printf("[Object] %s => %s\n", "point", redisTemplate.opsForValue().get("point"));
String value = new String(redisTemplate.getConnectionFactory().getConnection().get("point".getBytes()));
System.out.printf("[Raw] %s => %s\n", "point", value);
}
}
21:40:58.714 [main] INFO io.lettuce.core.EpollProvider - Starting without optional epoll library
21:40:58.801 [main] INFO io.lettuce.core.KqueueProvider - Starting with kqueue library
[Object] point => java.awt.Point[x=0,y=1]
[Raw] point => {"@class":"java.awt.Point","x":0.0,"y":1.0}