springboot 整合Redis
4. RedisTemplate原理
4.1 CacheAutoConfiguration
首先,在application的refresh生成组件的阶段,会对在Application类上的如@Srpingboot和@MapperScan@EnableCaching依据顺序执行,而@EnableCaching的官方注解为
/* In both of the scenarios above, {@code @EnableCaching} and {@code
* <cache:annotation-driven/>} are responsible for registering the necessary Spring
* components that power annotation-driven cache management, such as the
* {@link org.springframework.cache.interceptor.CacheInterceptor CacheInterceptor} and the
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
* {@link org.springframework.cache.annotation.Cacheable @Cacheable} methods are invoked.
*/
//即会注册相应的CacheInterceptor组件
该组件使得CacheAutoConfiguration
生效并加入到ioc容器中,该类若是使用@EnableCache则默认情况下会实现
//有了 proxyBeanMethods 属性后,配置类不会被代理了。主要是为了提高性能,如果你的 @Bean 方法之间没有调用关系的话可以把 proxyBeanMethods 设置为 false。否则,方法内部引用的类生产的类和 Spring 容器中类是两个类。
@Configuration(proxyBeanMethods = false)
// 查看是否有CacheManager的Class信息使得下文可以初始化
@ConditionalOnClass(CacheManager.class)
//查看是否有对@Cacheabe等注解的方法的类进行AOP增强的工具类
@ConditionalOnBean(CacheAspectSupport.class)
//查看是否注册了CacheManager在容器中,若是则该自动配置类失效
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import({ CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class })
public class CacheAutoConfiguration {
@Bean
@ConditionalOnMissingBean
// CacheManagerCustomizers,可用来加入容器以修改cacheManager初始化配置
public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) {
return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
}
@Bean
// CacheManagerValidator,可用来加入容器以修改cacheManager校验配置,在内部类实现
public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties,
ObjectProvider<CacheManager> cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
}
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
//后置处理器
static class CacheManagerEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
CacheManagerEntityManagerFactoryDependsOnPostProcessor() {
super("cacheManager");
}
}
/**
* Bean used to validate that a CacheManager exists and provide a more meaningful
* exception.
*/
static class CacheManagerValidator implements InitializingBean {
private final CacheProperties cacheProperties;
private final ObjectProvider<CacheManager> cacheManager;
CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
this.cacheProperties = cacheProperties;
this.cacheManager = cacheManager;
}
@Override
public void afterPropertiesSet() {
Assert.notNull(this.cacheManager.getIfAvailable(),
() -> "No cache manager could be auto-configured, check your configuration (caching type is '"
+ this.cacheProperties.getType() + "')");
}
}
/**
* {@link ImportSelector} to add {@link CacheType} configuration classes.
*/
//CacheConfigurations中有CacheType该枚举类中的值为key的spring包中存在的XXXCacheConfiguration.class。
// 将其取出,applicationContext实例化组件时按一定顺序实现(条件成立)
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
//getConfigurationClass为static方法,可直接调用
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
// 加载RedisAutoConfiguration,见4.2
}
4.2 RedisAutoConfiguration
若是我们使用的是redisNoSql,则我们从spring-boot-starter-data-redis中导入了spring-data-redis包,使得RedisAutoConfiguration
生效(redis有在springboot的AutoConfiguration JAr包中有自动配置类,Hazelcast等CacheConfigurations中的需要连接外部服务端的也有,但都需要导入相关依赖jar包中的资源才可以实例化)
Cache类型的AutoConfiguration依据设计有不同的功能,有的会实例化环境类进入ioc容器,而RedisAutoConfiguration其作用是配置Redis的客户端操作类RedisTemplate
。
@Configuration(proxyBeanMethods = false)
//只有实现了RedisOperations才能使得RedisAutoConfiguration实例化,而在spring-data-redis中才有,需要用starter导入,所以没加入默认不开启。
@ConditionalOnClass(RedisOperations.class)
//实例化Redis配置类的引用
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
···
}
Lettuce 和 Jedis 都是Redis的client,所以他们都可以连接 Redis Server。
Jedis在实现上是直接连接的Redis Server,如果在多线程环境下是非线程安全的。
每个线程都去拿自己的 Jedis 实例,当连接数量增多时,资源消耗阶梯式增大,连接成本就较高了。Lettuce的连接是基于Netty的,Netty 是一个多线程、事件驱动的 I/O 框架。连接实例可以在多个线程间共享,当多线程使用同一连接实例时,是线程安全的。
所以,一个多线程的应用可以使用同一个连接实例,而不用担心并发线程的数量。
当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。通过异步的方式可以让我们更好的利用系统资源,而不用浪费线程等待网络或磁盘I/O。
所以 Lettuce 可以帮助我们充分利用异步的优势。使用连接池,为每个Jedis实例增加物理连接Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
RedisAutoConfiguration
向容器中导入了两个类 RedisTemplate
和StringRedisTemplate
,作为Redis客户端分别操作k-v都为对象和k-v都为字符串的值。
factory中会创建且保存LettuceConnection
Conenction的创建会绑定一个Provider
Provider会绑定一个Client,Client才是对RedisServer的连接者
RedisTemplate
:主要是直接面对Redis数据库Server的CRUD操作,可以看成一个JAVA版本的客户端
- 采用Lettuce或者Jedis提供的对Redis数据库的connection,使得在java层面对connection进行增删查改即可对Redis数据库生效,只需面向connection进行编程配置实现想要的功能,并将需要得参数传入connection即可。
@Configuration(proxyBeanMethods = false)
//判断是否有引入org.springframework.data.redis.core
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
// 创建Lettuce(默认)类型的redisConnectionFactory(其中有对于Redis的pool以及pool管理,以及对RedisClient的创建,起作用如同JDbcConnectionFactory中线程池的思想,不过这变得 ),见4.3
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//只实现对默认RedisTemplate的实例化,见4.4
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//传入由LettuceConnectionConfiguration生成的redisConnectionFactory
//调用其相应得方法会自动从redisConnectionFactory中获取连接,只需往其中传入参数即可
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
// 这种xxxRedisTemplate其本质上仍是一个redisTemplate,只是配置得不同,最常见得便是序列化与反序列化的采用的json转换器的不同配置。
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
4.3 LettuceConnectionFactory
- Conenctionfactory作为template的参数保存在Template中,但不能直接使用,需要借助RedisConnectionUtils在有action到达时进行afterPropertiesSet的配置生成Client和connection、ConnectionProvider,
- 将Client绑定到ConnectionProvider上,在将ConnectionProvider绑定到connction上,使得Template及工具类可以面向Connection进行操作
- Client通过DefaultConnectionFuture进行Redis连接
public class LettuceConnectionFactory
implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
LettuceConverters.exceptionConverter());
private final Log log = LogFactory.getLog(getClass());
private final LettuceClientConfiguration clientConfiguration;
// 如同一个客户端对Redis的操作
private @Nullable AbstractRedisClient client;
// 对于客户端以何种方式执行client的工具类
private @Nullable LettuceConnectionProvider connectionProvider;
private @Nullable LettuceConnectionProvider reactiveConnectionProvider;
private boolean validateConnection = false;
private boolean shareNativeConnection = true;
private boolean eagerInitialization = false;
private @Nullable SharedConnection<byte[]> connection;
private @Nullable SharedConnection<ByteBuffer> reactiveConnection;
private @Nullable LettucePool pool;
/** Synchronization monitor for the shared Connection */
private final Object connectionMonitor = new Object();
private boolean convertPipelineAndTxResults = true;
private RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration("localhost", 6379);
private PipeliningFlushPolicy pipeliningFlushPolicy = PipeliningFlushPolicy.flushEachCommand();
private @Nullable RedisConfiguration configuration;
private @Nullable ClusterCommandExecutor clusterCommandExecutor;
// 默认用MutableLettuceClientConfiguration进行工厂客户端配置
public LettuceConnectionFactory() {
this(new MutableLettuceClientConfiguration());
}
//standaloneConfig 为Redis的默认端口的配置类
//clientConfig为Client配置类
private LettuceConnectionFactory(LettuceClientConfiguration clientConfig) {
Assert.notNull(clientConfig, "LettuceClientConfiguration must not be null!");
// 获取配置
this.clientConfiguration = clientConfig;
this.configuration = this.standaloneConfig;
}
public void afterPropertiesSet() {
// 新建客户端,为RedisClient的扩展类或RedisClusterClient,Client通过DefaultConnectionFuture进行Redis连接
this.client = createClient();
// 新建connectionProvider,默认为StandaloneConnectionProvider,主要功能为设置传入的Client以哪种形式对Redis进行连接
this.connectionProvider,默认为StandaloneConnectionProvider = new ExceptionTranslatingConnectionProvider(createConnectionProvider(client, CODEC));
this.reactiveConnectionProvider = new ExceptionTranslatingConnectionProvider(
createConnectionProvider(client, LettuceReactiveRedisConnection.CODEC));
if (isClusterAware()) {
this.clusterCommandExecutor = new ClusterCommandExecutor(
new LettuceClusterTopologyProvider((RedisClusterClient) client),
new LettuceClusterConnection.LettuceClusterNodeResourceProvider(this.connectionProvider),
EXCEPTION_TRANSLATION);
}
// 若是获取本地连接或者需要此时便初始化连接则初始化
if (getEagerInitialization() && getShareNativeConnection()) {
initConnection();
}
}
4.4 RedisTemplate
- RedisTemplate由两个较为重要的execute方法,其他的方法也是将参数处理后用这两个方法运行
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
//查看template是否初始化了,一般RedisTemplate不会直接使用,其会在StringRedisTemplate等的构造函数中调用afterPropertiesSet()方法初始化后initialized=true后才可以调用其中的execute方法
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
// 是否有需要执行的动作
Assert.notNull(action, "Callback object must not be null");
//获取连接工厂
RedisConnectionFactory factory = getRequiredConnectionFactory();
RedisConnection conn = null;
try {
if (enableTransactionSupport) {
// 是否开启了事务支持,若是则获取事务管理器中持有的连接
// only bind resources in case of potential transaction synchronization
conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
} else {
// 从工厂中获取一个redis连接
conn = RedisConnectionUtils.getConnection(factory);
}
// 查看事务管理器是否持有该工厂的连接
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
// 若是有则用事务连接中的
RedisConnection connToUse = preProcessConnection(conn, existingConnection);
// 是否支持管道操作
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
// 若是则开启
connToUse.openPipeline();
}
// 若工厂的连接池中有连接并获取到或者事务管理器中有保存连接,则获取,否则动态代理创建一个代理类
RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
// 通过获取的连接执行action到Redis中
//其后会用connection中的provider,再调用Client执行action
T result = action.doInRedis(connToExpose);
// close pipeline
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
// TODO: any other connection processing?
return postProcessResult(result, connToUse, existingConnection);
} finally {
// 断开连接返回连接池
RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport);
}
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
//默认使用JDK的序列化器
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
// 当key,value的序列化器为空时,给他们设置默认的序列化器
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<>(this);
}
initialized = true;
}
4. 自定义RedisCacheManager
RedisCacheManager在@Cacheable等注解时生效,也可独立使用,我们直接对Redis操作一般用template,但缓存的获取等我们一般让其自动化完成,所以RedisCacheManager的重要性才会上升,和Template分离
在导入redis依赖后RedisCacheConfiguration类就会自动生效,创建RedisCacheManager,并使用RedisCache进行缓存数据,要缓存的对象的类要实现Serializable接口,默认情况下是以jdk序列化数据存在redis中,如下:
k:"emp::1"
v:
\xAC\xED\x00\x05sr\x00$cn.edu.ustc.springboot.bean.Employeeuqf\x03p\x9A\xCF\xE0\x02\x00\x05L\x00\x03dIdt\x00\x13Ljava/lang/Integer;L\x00\x05emailt\x00\x12Ljava/lang/String;L\x00\x06genderq\x00~\x00\x01L\x00\x02idq\x00~\x00\x01L\x00\x08lastNameq\x00~\x00\x02xpsr\x00\x11java.lang.Integer\x12\xE2\xA0\xA4\xF7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xAC\x95\x1D\x0B\x94\xE0\x8B\x02\x00\x00xp\x00\x00\x00\x03t\x00\x07cch@aaasq\x00~\x00\x04\x00\x00\x00\x01q\x00~\x00\x08t\x00\x03cch
要想让对象以json形式存储在redis中,需要自定义RedisCacheManager,使用GenericJackson2JsonRedisSerializer类对value进行序列化。2.0版本后默认创建
@Configuration
public class MyRedisConfig {
@Bean
RedisCacheManager cacheManager(RedisConnectionFactory factory){
//创建默认RedisCacheWriter
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
//创建默认RedisCacheConfiguration并使用GenericJackson2JsonRedisSerializer构造的 SerializationPair对value进行转换
//创建GenericJackson2JsonRedisSerializer的json序列化器
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//使用json序列化器构造出对转换Object类型的SerializationPair序列化对
RedisSerializationContext.SerializationPair<Object> serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);
//将可以把Object转换为json的SerializationPair传入RedisCacheConfiguration
//使得RedisCacheConfiguration在转换value时使用定制序列化器
RedisCacheConfiguration cacheConfiguration=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(serializationPair);
RedisCacheManager cacheManager = new RedisCacheManager(cacheWriter,cacheConfiguration);
return cacheManager;
}
}
序列化数据如下:
k:"emp::3"
v:
{
"@class": "cn.edu.ustc.springboot.bean.Employee",
"id": 3,
"lastName": "aaa",
"email": "aaaa",
"gender": 1,
"dId": 5
}
注意,这里必须用GenericJackson2JsonRedisSerializer进行value的序列化解析,如果使用Jackson2JsonRedisSerializer,序列化的json没有 "@class": "cn.edu.ustc.springboot.bean.Employee"
,在读取缓存时会报类型转换异常。
5. RedisCacheManager原理
我们用AOP动态增强我们的service类,使得对@Cacheable
等的方法进行判断存储,调用时RedisCacheManager
会绑定到CacheAspectSupport
,CacheAspectSupport中的方法会到对应RedisCacheManager
的对应的cache中去查找
2.0版本以前,RedisCacheManager通过RedisTemplate前往redis进行CRUD操作,但在2.0版本后面,则出于解耦的考虑,将他们解耦开来。不然所有的配置都需要单独配置相应的Template来实现,使得每个template的复用情况下降
注意RedisCacheManager是Spring层面的管理类和RidisServer本身实现无关
5.1 RedisCacheConfiguration
@Configuration(proxyBeanMethods = false)
//仍需要RedisConnectionFactory,而他也在starter导入的jar包中,因此没添加依赖也默认不会加载
@ConditionalOnClass(RedisConnectionFactory.class)
// 为了防止别的同级别的RedisCacheConfiguration加载他们的RedisCacheManager,因此在RedisAutoConfiguration便加载,2.0后不需要RedisTemplate也可以实现注解的缓存,但要自己将工厂添加到容器中
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
//向容器中导入RedisCacheManager
@Bean
//cacheManager不同于以前,自己导入RedisConnectionFactory,能够自己获取connection进行操作
RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
//使用determineConfiguration()的返回值生成RedisCacheManagerBuilder,用DefaultRedisCacheWriter完成I/O操作,见4.2
//RedisCacheManager.builder将redisConnectionFactory放入DefaultRedisCacheWriter中,见4.3
//调用了RedisCacheManagerBuilder的cacheDefaults()方法返回以determineConfiguration生成的redisCacheConfiguration
//determineConfiguration为本类的方法,见下面
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
//使用RedisCacheManagerBuilder的build()方法创建RedisCacheManager并进行定制操作
return cacheManagerCustomizers.customize(builder.build());
}
//determineConfiguration,生成RedisCacheManagerBuilder用到的参数/
//注意此时的类是org.springframework.data.redis.cache.RedisCacheConfiguration,为导入的Redis的相关Jar包中的RedisCache配置类,用以配置cache初始化信息,因为与本类的方法同名,所以用全类名。
//之所以该类要用这个名字,是因为其他的生成RedisCacheManager的命名规范如此,诈胡
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
CacheProperties cacheProperties,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
ClassLoader classLoader) {
//determineConfiguration()调用了createConfiguration(),也在该类中
return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));
}
//createConfiguration()定义了其序列化value的规则,这个方法的作用与RedisTemplate中的afterPropertiesSet方法一样
//RedisCacheConfiguration见4.4
private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(
CacheProperties cacheProperties, ClassLoader classLoader) {
Redis redisProperties = cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
.defaultCacheConfig();
//使用jdk序列化器对value进行序列化
config = config.serializeValuesWith(
SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
//设置properties文件中设置的各项属性
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
//获取key时,是否加上前缀,一般前缀为Redis的一个命名空间
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
//是否运行cache中的value为空
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
//使用key时,是否加上前缀,一般前缀为Redis的一个命名空间
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
5.2 RedisCacheManager
RedisCacheManager的直接构造类,该类保存了配置类RedisCacheConfiguration,该配置在会传递给RedisCacheManager
public static class RedisCacheManagerBuilder {
private final RedisCacheWriter cacheWriter;
//默认缓存配置使用RedisCacheConfiguration的默认配置
//该默认配置缓存时默认将k按字符串存储,v按jdk序列化数据存储(见下一代码块)
private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
private boolean enableTransactions;
boolean allowInFlightCacheCreation = true;
private RedisCacheManagerBuilder(RedisCacheWriter cacheWriter) {
this.cacheWriter = cacheWriter;
}
//将connectionFactory放入DefaultRedisCacheWriter中,对redis的操作尤其接手,见4.3
public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
}
//传入RedisCacheManagerBuilder使用的缓存配置规则RedisCacheConfiguration类
public RedisCacheManagerBuilder cacheDefaults(RedisCacheConfiguration defaultCacheConfiguration) {
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
this.defaultCacheConfiguration = defaultCacheConfiguration;
return this;
}
//使用默认defaultCacheConfiguration创建RedisCacheManager
public RedisCacheManager build() {
RedisCacheManager cm = new RedisCacheManager(cacheWriter, defaultCacheConfiguration, initialCaches,allowInFlightCacheCreation);
cm.setTransactionAware(enableTransactions);
return cm;
}
5.3 DefaultRedisCacheWriter
- 其功能上有些像弱化版本的RedisTemplate
/*{@link RedisCacheWriter} implementation capable of reading/writing binary data from/to Redis in {@literal standalone}
* and {@literal cluster} environments. Works upon a given {@link RedisConnectionFactory} to obtain the actual*/
class DefaultRedisCacheWriter implements RedisCacheWriter {
@Override
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
execute(name, connection -> {
if (shouldExpireWithin(ttl)) {
connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());
} else {
connection.set(key, value);
}
return "OK";
});
}
//获取连接并执行操作
private <T> T execute(String name, Function<RedisConnection, T> callback) {
RedisConnection connection = connectionFactory.getConnection();
try {
checkAndPotentiallyWaitUntilUnlocked(name, connection);
return callback.apply(connection);
} finally {
connection.close();
}
}
}
5.4 RedisCacheConfiguration
RedisCacheConfiguration(导入的jar包中)保存了许多缓存规则,这些规则都保存在RedisCacheManagerBuilder的RedisCacheConfiguration defaultCacheConfiguration属性中,并且当RedisCacheManagerBuilder创建RedisCacheManager传递过去
public class RedisCacheConfiguration {
private final Duration ttl;
private final boolean cacheNullValues;
private final CacheKeyPrefix keyPrefix;
private final boolean usePrefix;
private final SerializationPair<String> keySerializationPair;
private final SerializationPair<Object> valueSerializationPair;
private final ConversionService conversionService;
//默认缓存配置
public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader classLoader) {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
registerDefaultConverters(conversionService);
return new RedisCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(),
SerializationPair.fromSerializer(RedisSerializer.string()),
//key使用字符串
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
//value按jdk序列化存储
}
5.5 RedisCacheManager创建cache
RedisCacheManager在创建RedisCache时将RedisCacheConfiguration传递过去,并在创建RedisCache时通过createRedisCache()起作用
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
private final boolean allowInFlightCacheCreation;
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
// 每一个cache的创建都加入了RedisCacheManager创建时放入了RedisFactory的cacheWriter
//如果调用该方法时RedisCacheConfiguration有值则使用定制的,否则则使用默认的RedisCacheConfiguration defaultCacheConfig,即RedisCacheManagerBuilder传递过来的配置,见4.6
return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
5.6 RedisCache
每一个RedisCache的操作
RedisCache,Redis缓存,具体负责将缓存数据序列化的地方,将RedisCacheConfiguration的序列化对SerializationPair提取出来并使用其定义的序列化方式分别对k和v进行序列化操作
public class RedisCache extends AbstractValueAdaptingCache {
private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);
private final String name;
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration cacheConfig;
private final ConversionService conversionService;
public void put(Object key, @Nullable Object value) {
Object cacheValue = preProcessCacheValue(value);
if (!isAllowNullValues() && cacheValue == null) {
throw new IllegalArgumentException(String.format(
"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
name));
}
//在put k-v时使用cacheConfig中的k-v序列化器分别对k-v进行序列化
//均是用cacheWriter获取连接再用RedisClient和RedisProvide等进行操作,与RedisTemplate相似
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}
//从cacheConfig(即RedisCacheConfiguration)中获取KeySerializationPair并写入key值
protected byte[] serializeCacheKey(String cacheKey) {
return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
}
//从cacheConfig(即RedisCacheConfiguration)中获取ValueSerializationPair并写入key值
protected byte[] serializeCacheValue(Object value) {
if (isAllowNullValues() && value instanceof NullValue) {
return BINARY_NULL_VALUE;
}
return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
}
分析到这也就不难理解,要使用json保存序列化数据时,需要自定义RedisCacheManager,在RedisCacheConfiguration中定义序列化转化规则,并向RedisCacheManager传入我们自己定制的RedisCacheConfiguration了,我定制的序列化规则会跟随RedisCacheConfiguration一直传递到RedisCache,并在序列化时发挥作用。