zoukankan      html  css  js  c++  java
  • 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向容器中导入了两个类 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:
    xACxEDx00x05srx00$cn.edu.ustc.springboot.bean.Employeeuqfx03px9AxCFxE0x02x00x05Lx00x03dIdtx00x13Ljava/lang/Integer;Lx00x05emailtx00x12Ljava/lang/String;Lx00x06genderqx00~x00x01Lx00x02idqx00~x00x01Lx00x08lastNameqx00~x00x02xpsrx00x11java.lang.Integerx12xE2xA0xA4xF7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xACx95x1Dx0Bx94xE0x8Bx02x00x00xpx00x00x00x03tx00x07cch@aaasqx00~x00x04x00x00x00x01qx00~x00x08tx00x03cch
    

    要想让对象以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,并在序列化时发挥作用。

  • 相关阅读:
    Ubuntu18下解决和IDEA快捷键冲突
    SM.SM图床踩坑记录
    springboot热部署
    springboot创建项目报错
    三次握手,四次挥手(转载)
    slf4j和log4j2整合报错,绑定失败
    Linux终端光标消失解决
    ssh免密登录
    Spring中编写单元测试
    用js模拟查询方法 hide() filter(":contains('"
  • 原文地址:https://www.cnblogs.com/eternal-heathens/p/13603589.html
Copyright © 2011-2022 走看看