ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

springboot 整合Redis

2020-09-02 19:32:53  阅读:251  来源: 互联网

标签:RedisCacheManager springboot 序列化 Redis private RedisCacheConfiguration 整合 x00 cl


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向容器中导入了两个类 RedisTemplateStringRedisTemplate,作为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会绑定到CacheAspectSupportCacheAspectSupport中的方法会到对应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,并在序列化时发挥作用。

标签:RedisCacheManager,springboot,序列化,Redis,private,RedisCacheConfiguration,整合,x00,cl
来源: https://www.cnblogs.com/eternal-heathens/p/13603589.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有