zoukankan      html  css  js  c++  java
  • 006-spring cache-缓存原理-SimpleCacheConfiguration、RedisCacheConfiguration原理实现

    一、实现说明

    事务回滚:除了GuavaCacheManager之外,其他Cache都支持Spring事务,即如果事务回滚了,缓存的数据也会移除掉。

    Spring不进行cache的定义,也不进行Cache的缓存策略的维护。这些都是由底层cache自己实现,然后外部创建一个cache注入进来的。Spring只是提供了一个Wrapper,提供一套对外一致的API。

    CacheManger描述
    SimpleCacheManager 使用简单的Collection来存储缓存,主要用于测试
    ConcurrentMapCacheManager 使用ConcurrentMap作为缓存技术(默认)
    NoOpCacheManager 测试用
    EhCacheCacheManager 使用EhCache作为缓存技术,以前在hibernate的时候经常用
    GuavaCacheManager 使用google guava的GuavaCache作为缓存技术
    HazelcastCacheManager 使用Hazelcast作为缓存技术
    JCacheCacheManager 使用JCache标准的实现作为缓存技术,如Apache Commons JCS
    RedisCacheManager 使用Redis作为缓存技术

    springboot cache

        static {
            Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
            mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
            mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
            mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
            mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
            mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
            mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
            mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
            mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
            mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
            mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
            MAPPINGS = Collections.unmodifiableMap(mappings);
        }

      Spring boot默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager来实现缓存。

    二、CacheManager说明

    2.1、SimpleCacheConfiguration 说明

    核心UML图

      

    查看ConcurrentMapCache实现 核心是ValueWrapper

      

    2.1.1、SimpleCacheConfiguration说明实现

    代码地址:https://github.com/bjlhx15/common.git spring-cache/springboot2-cache-default

    2.1.2、基础配置

    参看:001-spring cache 简介、基础使用

    2.2、RedisCacheConfiguration说明 

        

    1、RedisCacheConfiguration

      配置类配置缓存管理器

    /*
     * Copyright 2012-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot.autoconfigure.cache;
    
    import java.util.LinkedHashSet;
    import java.util.List;
    
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.cache.CacheProperties.Redis;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
    import org.springframework.cache.CacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
    
    /**
     * Redis cache configuration.
     *
     * @author Stephane Nicoll
     * @author Mark Paluch
     * @author Ryon Day
     * @since 1.3.0
     */
    @Configuration
    @ConditionalOnClass(RedisConnectionFactory.class)
    @AutoConfigureAfter(RedisAutoConfiguration.class)
    @ConditionalOnBean(RedisConnectionFactory.class)
    @ConditionalOnMissingBean(CacheManager.class)
    @Conditional(CacheCondition.class)
    class RedisCacheConfiguration {
    
        private final CacheProperties cacheProperties;
    
        private final CacheManagerCustomizers customizerInvoker;
    
        private final org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration;
    
        RedisCacheConfiguration(CacheProperties cacheProperties,
                CacheManagerCustomizers customizerInvoker,
                ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration) {
            this.cacheProperties = cacheProperties;
            this.customizerInvoker = customizerInvoker;
            this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable();
        }
    
        @Bean
        public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
                ResourceLoader resourceLoader) {
            RedisCacheManagerBuilder builder = RedisCacheManager
                    .builder(redisConnectionFactory)
                    .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
            List<String> cacheNames = this.cacheProperties.getCacheNames();
            if (!cacheNames.isEmpty()) {
                builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
            }
            return this.customizerInvoker.customize(builder.build());
        }
    
        private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
                ClassLoader classLoader) {
            if (this.redisCacheConfiguration != null) {
                return this.redisCacheConfiguration;
            }
            Redis redisProperties = this.cacheProperties.getRedis();
            org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
                    .defaultCacheConfig();
            config = config.serializeValuesWith(SerializationPair
                    .fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
            if (redisProperties.getTimeToLive() != null) {
                config = config.entryTtl(redisProperties.getTimeToLive());
            }
            if (redisProperties.getKeyPrefix() != null) {
                config = config.prefixKeysWith(redisProperties.getKeyPrefix());
            }
            if (!redisProperties.isCacheNullValues()) {
                config = config.disableCachingNullValues();
            }
            if (!redisProperties.isUseKeyPrefix()) {
                config = config.disableKeyPrefix();
            }
            return config;
        }
    
    }
    View Code

      注入方法

        @Bean
        public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
                ResourceLoader resourceLoader) {
            RedisCacheManagerBuilder builder = RedisCacheManager
                    .builder(redisConnectionFactory)
                    .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
            List<String> cacheNames = this.cacheProperties.getCacheNames();
            if (!cacheNames.isEmpty()) {
                builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
            }
            return this.customizerInvoker.customize(builder.build());
        }

      可以看到 核心是 创建了一个 RedisCacheManager,同时使用:initialCacheNames(new LinkedHashSet<>(cacheNames));

    2、RedisCacheManager

      继承关系:RedisCacheManager→AbstractTransactionSupportingCacheManager【事务支持抽象类】→ AbstractCacheManager→CacheManager、InitializingBean

      CacheManager:提供原始管理接口方法

      InitializingBean:主要作用是Bean初始化后执行一个afterPropertiesSet方法 003-Spring4 扩展分析-spring类初始化@PostConstruct > InitializingBean > init-method、ApplicationContext、BeanPostProcessor、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor

      AbstractCacheManager:afterPropertiesSet中调用 initializeCaches

        public void initializeCaches() {
            Collection<? extends Cache> caches = loadCaches();
    
            synchronized (this.cacheMap) {
                this.cacheNames = Collections.emptySet();
                this.cacheMap.clear();
                Set<String> cacheNames = new LinkedHashSet<>(caches.size());
                for (Cache cache : caches) {
                    String name = cache.getName();
                    this.cacheMap.put(name, decorateCache(cache));
                    cacheNames.add(name);
                }
                this.cacheNames = Collections.unmodifiableSet(cacheNames);
            }
        }

        其一调用了:loadCaches;其二 初始化缓存名

      RedisCacheManager主要管理缓存

    /*
     * Copyright 2017-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.springframework.data.redis.cache;
    
    import java.util.Collection;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.lang.Nullable;
    import org.springframework.util.Assert;
    
    /**
     * {@link org.springframework.cache.CacheManager} backed by a {@link RedisCache Redis} cache.
     * <p />
     * This cache manager creates caches by default upon first write. Empty caches are not visible on Redis due to how Redis
     * represents empty data structures.
     * <p />
     * Caches requiring a different {@link RedisCacheConfiguration} than the default configuration can be specified via
     * {@link RedisCacheManagerBuilder#withInitialCacheConfigurations(Map)}.
     *
     * @author Christoph Strobl
     * @author Mark Paluch
     * @since 2.0
     * @see RedisCacheConfiguration
     * @see RedisCacheWriter
     */
    public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
    
        private final RedisCacheWriter cacheWriter;
        private final RedisCacheConfiguration defaultCacheConfig;
        private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
        private final boolean allowInFlightCacheCreation;
    
        /**
         * Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
         * {@link RedisCacheConfiguration}.
         *
         * @param cacheWriter must not be {@literal null}.
         * @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
         *          {@link RedisCacheConfiguration#defaultCacheConfig()}.
         * @param allowInFlightCacheCreation allow create unconfigured caches.
         * @since 2.0.4
         */
        private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
                boolean allowInFlightCacheCreation) {
    
            Assert.notNull(cacheWriter, "CacheWriter must not be null!");
            Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
    
            this.cacheWriter = cacheWriter;
            this.defaultCacheConfig = defaultCacheConfiguration;
            this.initialCacheConfiguration = new LinkedHashMap<>();
            this.allowInFlightCacheCreation = allowInFlightCacheCreation;
        }
    
        /**
         * Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
         * {@link RedisCacheConfiguration}.
         *
         * @param cacheWriter must not be {@literal null}.
         * @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
         *          {@link RedisCacheConfiguration#defaultCacheConfig()}.
         */
        public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
            this(cacheWriter, defaultCacheConfiguration, true);
        }
    
        /**
         * Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
         * {@link RedisCacheConfiguration}.
         *
         * @param cacheWriter must not be {@literal null}.
         * @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
         *          {@link RedisCacheConfiguration#defaultCacheConfig()}.
         * @param initialCacheNames optional set of known cache names that will be created with given
         *          {@literal defaultCacheConfiguration}.
         */
        public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
                String... initialCacheNames) {
    
            this(cacheWriter, defaultCacheConfiguration, true, initialCacheNames);
        }
    
        /**
         * Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
         * {@link RedisCacheConfiguration}.
         *
         * @param cacheWriter must not be {@literal null}.
         * @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
         *          {@link RedisCacheConfiguration#defaultCacheConfig()}.
         * @param allowInFlightCacheCreation if set to {@literal true} no new caches can be acquire at runtime but limited to
         *          the given list of initial cache names.
         * @param initialCacheNames optional set of known cache names that will be created with given
         *          {@literal defaultCacheConfiguration}.
         * @since 2.0.4
         */
        public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
                boolean allowInFlightCacheCreation, String... initialCacheNames) {
    
            this(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation);
    
            for (String cacheName : initialCacheNames) {
                this.initialCacheConfiguration.put(cacheName, defaultCacheConfiguration);
            }
        }
    
        /**
         * Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
         * {@link RedisCacheConfiguration}.
         *
         * @param cacheWriter must not be {@literal null}.
         * @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
         *          {@link RedisCacheConfiguration#defaultCacheConfig()}.
         * @param initialCacheConfigurations Map of known cache names along with the configuration to use for those caches.
         *          Must not be {@literal null}.
         */
        public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
                Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
    
            this(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, true);
        }
    
        /**
         * Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
         * {@link RedisCacheConfiguration}.
         *
         * @param cacheWriter must not be {@literal null}.
         * @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
         *          {@link RedisCacheConfiguration#defaultCacheConfig()}.
         * @param initialCacheConfigurations Map of known cache names along with the configuration to use for those caches.
         *          Must not be {@literal null}.
         * @param allowInFlightCacheCreation if set to {@literal false} this cache manager is limited to the initial cache
         *          configurations and will not create new caches at runtime.
         * @since 2.0.4
         */
        public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
                Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
    
            this(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation);
    
            Assert.notNull(initialCacheConfigurations, "InitialCacheConfigurations must not be null!");
    
            this.initialCacheConfiguration.putAll(initialCacheConfigurations);
        }
    
        /**
         * Create a new {@link RedisCacheManager} with defaults applied.
         * <dl>
         * <dt>locking</dt>
         * <dd>disabled</dd>
         * <dt>cache configuration</dt>
         * <dd>{@link RedisCacheConfiguration#defaultCacheConfig()}</dd>
         * <dt>initial caches</dt>
         * <dd>none</dd>
         * <dt>transaction aware</dt>
         * <dd>no</dd>
         * <dt>in-flight cache creation</dt>
         * <dd>enabled</dd>
         * </dl>
         *
         * @param connectionFactory must not be {@literal null}.
         * @return new instance of {@link RedisCacheManager}.
         */
        public static RedisCacheManager create(RedisConnectionFactory connectionFactory) {
    
            Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
    
            return new RedisCacheManager(new DefaultRedisCacheWriter(connectionFactory),
                    RedisCacheConfiguration.defaultCacheConfig());
        }
    
        /**
         * Entry point for builder style {@link RedisCacheManager} configuration.
         *
         * @param connectionFactory must not be {@literal null}.
         * @return new {@link RedisCacheManagerBuilder}.
         */
        public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
    
            Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
    
            return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
        }
    
        /**
         * Entry point for builder style {@link RedisCacheManager} configuration.
         *
         * @param cacheWriter must not be {@literal null}.
         * @return new {@link RedisCacheManagerBuilder}.
         */
        public static RedisCacheManagerBuilder builder(RedisCacheWriter cacheWriter) {
    
            Assert.notNull(cacheWriter, "CacheWriter must not be null!");
    
            return RedisCacheManagerBuilder.fromCacheWriter(cacheWriter);
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.support.AbstractCacheManager#loadCaches()
         */
        @Override
        protected Collection<RedisCache> loadCaches() {
    
            List<RedisCache> caches = new LinkedList<>();
    
            for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
                caches.add(createRedisCache(entry.getKey(), entry.getValue()));
            }
    
            return caches;
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.support.AbstractCacheManager#getMissingCache(java.lang.String)
         */
        @Override
        protected RedisCache getMissingCache(String name) {
            return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
        }
    
        /**
         * @return unmodifiable {@link Map} containing cache name / configuration pairs. Never {@literal null}.
         */
        public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
    
            Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(getCacheNames().size());
    
            getCacheNames().forEach(it -> {
    
                RedisCache cache = RedisCache.class.cast(lookupCache(it));
                configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
            });
    
            return Collections.unmodifiableMap(configurationMap);
        }
    
        /**
         * Configuration hook for creating {@link RedisCache} with given name and {@code cacheConfig}.
         *
         * @param name must not be {@literal null}.
         * @param cacheConfig can be {@literal null}.
         * @return never {@literal null}.
         */
        protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
            return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
        }
    
        /**
         * Configurator for creating {@link RedisCacheManager}.
         *
         * @author Christoph Strobl
         * @author Mark Strobl
         * @author Kezhu Wang
         * @since 2.0
         */
        public static class RedisCacheManagerBuilder {
    
            private final RedisCacheWriter cacheWriter;
            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;
            }
    
            /**
             * Entry point for builder style {@link RedisCacheManager} configuration.
             *
             * @param connectionFactory must not be {@literal null}.
             * @return new {@link RedisCacheManagerBuilder}.
             */
            public static RedisCacheManagerBuilder fromConnectionFactory(RedisConnectionFactory connectionFactory) {
    
                Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
    
                return builder(new DefaultRedisCacheWriter(connectionFactory));
            }
    
            /**
             * Entry point for builder style {@link RedisCacheManager} configuration.
             *
             * @param cacheWriter must not be {@literal null}.
             * @return new {@link RedisCacheManagerBuilder}.
             */
            public static RedisCacheManagerBuilder fromCacheWriter(RedisCacheWriter cacheWriter) {
    
                Assert.notNull(cacheWriter, "CacheWriter must not be null!");
    
                return new RedisCacheManagerBuilder(cacheWriter);
            }
    
            /**
             * Define a default {@link RedisCacheConfiguration} applied to dynamically created {@link RedisCache}s.
             *
             * @param defaultCacheConfiguration must not be {@literal null}.
             * @return this {@link RedisCacheManagerBuilder}.
             */
            public RedisCacheManagerBuilder cacheDefaults(RedisCacheConfiguration defaultCacheConfiguration) {
    
                Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
    
                this.defaultCacheConfiguration = defaultCacheConfiguration;
    
                return this;
            }
    
            /**
             * Enable {@link RedisCache}s to synchronize cache put/evict operations with ongoing Spring-managed transactions.
             *
             * @return this {@link RedisCacheManagerBuilder}.
             */
            public RedisCacheManagerBuilder transactionAware() {
    
                this.enableTransactions = true;
    
                return this;
            }
    
            /**
             * Append a {@link Set} of cache names to be pre initialized with current {@link RedisCacheConfiguration}.
             * <strong>NOTE:</strong> This calls depends on {@link #cacheDefaults(RedisCacheConfiguration)} using whatever
             * default {@link RedisCacheConfiguration} is present at the time of invoking this method.
             *
             * @param cacheNames must not be {@literal null}.
             * @return this {@link RedisCacheManagerBuilder}.
             */
            public RedisCacheManagerBuilder initialCacheNames(Set<String> cacheNames) {
    
                Assert.notNull(cacheNames, "CacheNames must not be null!");
    
                Map<String, RedisCacheConfiguration> cacheConfigMap = new LinkedHashMap<>(cacheNames.size());
                cacheNames.forEach(it -> cacheConfigMap.put(it, defaultCacheConfiguration));
    
                return withInitialCacheConfigurations(cacheConfigMap);
            }
    
            /**
             * Append a {@link Map} of cache name/{@link RedisCacheConfiguration} pairs to be pre initialized.
             *
             * @param cacheConfigurations must not be {@literal null}.
             * @return this {@link RedisCacheManagerBuilder}.
             */
            public RedisCacheManagerBuilder withInitialCacheConfigurations(
                    Map<String, RedisCacheConfiguration> cacheConfigurations) {
    
                Assert.notNull(cacheConfigurations, "CacheConfigurations must not be null!");
                cacheConfigurations.forEach((cacheName, configuration) -> Assert.notNull(configuration,
                        String.format("RedisCacheConfiguration for cache %s must not be null!", cacheName)));
    
                this.initialCaches.putAll(cacheConfigurations);
    
                return this;
            }
    
            /**
             * Disable in-flight {@link org.springframework.cache.Cache} creation for unconfigured caches.
             * <p />
             * {@link RedisCacheManager#getMissingCache(String)} returns {@literal null} for any unconfigured
             * {@link org.springframework.cache.Cache} instead of a new {@link RedisCache} instance. This allows eg.
             * {@link org.springframework.cache.support.CompositeCacheManager} to chime in.
             *
             * @return this {@link RedisCacheManagerBuilder}.
             * @since 2.0.4
             */
            public RedisCacheManagerBuilder disableCreateOnMissingCache() {
    
                this.allowInFlightCacheCreation = false;
                return this;
            }
    
            /**
             * Create new instance of {@link RedisCacheManager} with configuration options applied.
             *
             * @return new instance of {@link RedisCacheManager}.
             */
            public RedisCacheManager build() {
    
                RedisCacheManager cm = new RedisCacheManager(cacheWriter, defaultCacheConfiguration, initialCaches,
                        allowInFlightCacheCreation);
    
                cm.setTransactionAware(enableTransactions);
    
                return cm;
            }
        }
    }
    View Code

        这里使用了建造者模式:002-创建型-04-建造者模式(Builder)、JDK1.7源码中的建造者模式、Spring中的建造者模式

        这里重写了:Collection<RedisCache> loadCaches()

        @Override
        protected Collection<RedisCache> loadCaches() {
            List<RedisCache> caches = new LinkedList<>();
            for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
                caches.add(createRedisCache(entry.getKey(), entry.getValue()));
            }
            return caches;
        } 

      所以在Bean初始化后会加载 RedisCache

    3、RedisCache

    直接看代码

    /*
     * Copyright 2017-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.springframework.data.redis.cache;
    
    import java.lang.reflect.Method;
    import java.nio.ByteBuffer;
    import java.util.concurrent.Callable;
    
    import org.springframework.cache.support.AbstractValueAdaptingCache;
    import org.springframework.cache.support.NullValue;
    import org.springframework.cache.support.SimpleValueWrapper;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.core.convert.TypeDescriptor;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.util.ByteUtils;
    import org.springframework.lang.Nullable;
    import org.springframework.util.Assert;
    import org.springframework.util.ObjectUtils;
    import org.springframework.util.ReflectionUtils;
    
    /**
     * {@link org.springframework.cache.Cache} implementation using for Redis as underlying store.
     * <p />
     * Use {@link RedisCacheManager} to create {@link RedisCache} instances.
     *
     * @author Christoph Strobl
     * @author Mark Paluch
     * @since 2.0
     * @see RedisCacheConfiguration
     * @see RedisCacheWriter
     */
    public class RedisCache extends AbstractValueAdaptingCache {
    
        private static final byte[] BINARY_NULL_VALUE = new JdkSerializationRedisSerializer().serialize(NullValue.INSTANCE);
    
        private final String name;
        private final RedisCacheWriter cacheWriter;
        private final RedisCacheConfiguration cacheConfig;
        private final ConversionService conversionService;
    
        /**
         * Create new {@link RedisCache}.
         *
         * @param name must not be {@literal null}.
         * @param cacheWriter must not be {@literal null}.
         * @param cacheConfig must not be {@literal null}.
         */
        protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
    
            super(cacheConfig.getAllowCacheNullValues());
    
            Assert.notNull(name, "Name must not be null!");
            Assert.notNull(cacheWriter, "CacheWriter must not be null!");
            Assert.notNull(cacheConfig, "CacheConfig must not be null!");
    
            this.name = name;
            this.cacheWriter = cacheWriter;
            this.cacheConfig = cacheConfig;
            this.conversionService = cacheConfig.getConversionService();
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.support.AbstractValueAdaptingCache#lookup(java.lang.Object)
         */
        @Override
        protected Object lookup(Object key) {
    
            byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
    
            if (value == null) {
                return null;
            }
    
            return deserializeCacheValue(value);
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#getName()
         */
        @Override
        public String getName() {
            return name;
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#getNativeCache()
         */
        @Override
        public RedisCacheWriter getNativeCache() {
            return this.cacheWriter;
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#get(java.lang.Object, java.util.concurrent.Callable)
         */
        @Override
        @SuppressWarnings("unchecked")
        public synchronized <T> T get(Object key, Callable<T> valueLoader) {
    
            ValueWrapper result = get(key);
    
            if (result != null) {
                return (T) result.get();
            }
    
            T value = valueFromLoader(key, valueLoader);
            put(key, value);
            return value;
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
         */
        @Override
        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));
            }
    
            cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object, java.lang.Object)
         */
        @Override
        public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
    
            Object cacheValue = preProcessCacheValue(value);
    
            if (!isAllowNullValues() && cacheValue == null) {
                return get(key);
            }
    
            byte[] result = cacheWriter.putIfAbsent(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue),
                    cacheConfig.getTtl());
    
            if (result == null) {
                return null;
            }
    
            return new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result)));
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#evict(java.lang.Object)
         */
        @Override
        public void evict(Object key) {
            cacheWriter.remove(name, createAndConvertCacheKey(key));
        }
    
        /*
         * (non-Javadoc)
         * @see org.springframework.cache.Cache#clear()
         */
        @Override
        public void clear() {
    
            byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
            cacheWriter.clean(name, pattern);
        }
    
        /**
         * Get {@link RedisCacheConfiguration} used.
         *
         * @return immutable {@link RedisCacheConfiguration}. Never {@literal null}.
         */
        public RedisCacheConfiguration getCacheConfiguration() {
            return cacheConfig;
        }
    
        /**
         * Customization hook called before passing object to
         * {@link org.springframework.data.redis.serializer.RedisSerializer}.
         *
         * @param value can be {@literal null}.
         * @return preprocessed value. Can be {@literal null}.
         */
        @Nullable
        protected Object preProcessCacheValue(@Nullable Object value) {
    
            if (value != null) {
                return value;
            }
    
            return isAllowNullValues() ? NullValue.INSTANCE : null;
        }
    
        /**
         * Serialize the key.
         *
         * @param cacheKey must not be {@literal null}.
         * @return never {@literal null}.
         */
        protected byte[] serializeCacheKey(String cacheKey) {
            return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
        }
    
        /**
         * Serialize the value to cache.
         *
         * @param value must not be {@literal null}.
         * @return never {@literal null}.
         */
        protected byte[] serializeCacheValue(Object value) {
    
            if (isAllowNullValues() && value instanceof NullValue) {
                return BINARY_NULL_VALUE;
            }
    
            return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
        }
    
        /**
         * Deserialize the given value to the actual cache value.
         *
         * @param value must not be {@literal null}.
         * @return can be {@literal null}.
         */
        @Nullable
        protected Object deserializeCacheValue(byte[] value) {
    
            if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
                return NullValue.INSTANCE;
            }
    
            return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
        }
    
        /**
         * Customization hook for creating cache key before it gets serialized.
         *
         * @param key will never be {@literal null}.
         * @return never {@literal null}.
         */
        protected String createCacheKey(Object key) {
    
            String convertedKey = convertKey(key);
    
            if (!cacheConfig.usePrefix()) {
                return convertedKey;
            }
    
            return prefixCacheKey(convertedKey);
        }
    
        /**
         * Convert {@code key} to a {@link String} representation used for cache key creation.
         *
         * @param key will never be {@literal null}.
         * @return never {@literal null}.
         * @throws IllegalStateException if {@code key} cannot be converted to {@link String}.
         */
        protected String convertKey(Object key) {
    
            TypeDescriptor source = TypeDescriptor.forObject(key);
            if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
                return conversionService.convert(key, String.class);
            }
    
            Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
    
            if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
                return key.toString();
            }
    
            throw new IllegalStateException(
                    String.format("Cannot convert %s to String. Register a Converter or override toString().", source));
        }
    
        private byte[] createAndConvertCacheKey(Object key) {
            return serializeCacheKey(createCacheKey(key));
        }
    
        private String prefixCacheKey(String key) {
    
            // allow contextual cache names by computing the key prefix on every call.
            return cacheConfig.getKeyPrefixFor(name) + key;
        }
    
        private static <T> T valueFromLoader(Object key, Callable<T> valueLoader) {
    
            try {
                return valueLoader.call();
            } catch (Exception e) {
                throw new ValueRetrievalException(key, valueLoader, e);
            }
        }
    }
    View Code

     说明

    private static final byte[] BINARY_NULL_VALUE = new JdkSerializationRedisSerializer().serialize(NullValue.INSTANCE);
    
        private final String name; //缓存空间名
        private final RedisCacheWriter cacheWriter; //redis操作对象
        private final RedisCacheConfiguration cacheConfig;  //缓存的配置 与springboot里面的不一样
        private final ConversionService conversionService;

    1、lookup(Object key) 取值

      为了父类的 :ValueWrapper get(Object key); 以及<T> T get(Object key, @Nullable Class<T> type);使用 这两个是Cache 接口要求的

        @Override
        protected Object lookup(Object key) {
            byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
            if (value == null) {
                return null;
            }
            return deserializeCacheValue(value);
        }

      在基础存储中执行实际查找。

      说明:lookup 简单理解,向上看给上面用的,然后翻看RedisCache的 AbstractValueAdaptingCache 父类。

        @Override
        public ValueWrapper get(Object key) {
            Object value = lookup(key);
            return toValueWrapper(value);
        }
        @Overridepublic <T> T get(Object key, @Nullable Class<T> type) {
            Object value = fromStoreValue(lookup(key));
            if (value != null && type != null && !type.isInstance(value)) {
                throw new IllegalStateException(
                        "Cached value is not of required type [" + type.getName() + "]: " + value);
            }
            return (T) value;
        }
        @Nullable
        protected abstract Object lookup(Object key);

      原来如此,标准模板方法 004-行为型-02-模板方法模式(Template Method)

      其一、ValueWrapper  get(Object key) 转换成ValueWrapper

        toValueWrapper

        protected Cache.ValueWrapper toValueWrapper(@Nullable Object storeValue) {
            return (storeValue != null ? new SimpleValueWrapper(fromStoreValue(storeValue)) : null);
        }

          fromStoreValue 判空,返回原值

        protected Object fromStoreValue(@Nullable Object storeValue) {
            if (this.allowNullValues && storeValue == NullValue.INSTANCE) {
                return null;
            }
            return storeValue;
        } 

          SimpleValueWrapper,其实也是没做任何事,只是ValueWrapper一下

    public class SimpleValueWrapper implements ValueWrapper {
        @Nullable
        private final Object value;
        public SimpleValueWrapper(@Nullable Object value) {
            this.value = value;
        }
        @Override
        public Object get() {
            return this.value;
        }
    }

      其二、T get(Object key, @Nullable Class<T> type) 转换成 具体类对象

        取值,判空,强制转换

    2、String getName() 缓存空间名

    3、RedisCacheWriter getNativeCache()  返回底层本机缓存提供程序。

    4、synchronized <T> T get(Object key, Callable<T> valueLoader) 返回缓存值

        @Override
        @SuppressWarnings("unchecked")
        public synchronized <T> T get(Object key, Callable<T> valueLoader) {
            ValueWrapper result = get(key);
            if (result != null) {
                return (T) result.get();
            }
            T value = valueFromLoader(key, valueLoader);
            put(key, value);
            return value;
        }

      首先调用父类的get,获取wrapper,有值强转回具体对象,否则会 valueFromLoader

      valueFromLoader,从传入的方法loader加载value,

        private static <T> T valueFromLoader(Object key, Callable<T> valueLoader) {
            return valueLoader.call();
        }

      接下来put存入,返回具体对象

    5、put(Object key, @Nullable Object value) 存值

        //按key 缓存 值
        @Override
        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));
            }
            cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
        }
    View Code

    6、ValueWrapper putIfAbsent(Object key, @Nullable Object value)   如果传入key对应的value已经存在,就返回存在的value,不进行替换。如果不存在,就添加key和value,返回null.

        /*
         * <pre><code>
         * Object existingValue = cache.get(key);
         * if (existingValue == null) {
         *     cache.put(key, value);
         *     return null;
         * } else {
         *     return existingValue;
         * }
         * </code></pre>
         */
        @Override
        public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
            Object cacheValue = preProcessCacheValue(value); //空值处理
            if (!isAllowNullValues() && cacheValue == null) {
                return get(key);//通过key去value值
            }
            byte[] result = cacheWriter.putIfAbsent(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue),  cacheConfig.getTtl());//利用 缓存提供者 提供的处理
    
            if (result == null) {
                return null;
            }
            return new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result)));
        }
    View Code

    7、evict(Object key) 清除 指定key缓存

        //清除 指定key缓存
        @Override
        public void evict(Object key) {
            cacheWriter.remove(name, createAndConvertCacheKey(key));
        }

      createAndConvertCacheKey,以及一些列的key处理

        //序列化key
        protected byte[] serializeCacheKey(String cacheKey) {
            return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
        }
    
        //序列化值
        protected byte[] serializeCacheValue(Object value) {
            if (isAllowNullValues() && value instanceof NullValue) {
                return BINARY_NULL_VALUE;
            }
            return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
        }
    
        //反序列化 值
        @Nullable
        protected Object deserializeCacheValue(byte[] value) {
            if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
                return NullValue.INSTANCE;
            }
            return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
        }
    
        //key处理
        protected String createCacheKey(Object key) {
            String convertedKey = convertKey(key);
            if (!cacheConfig.usePrefix()) {
                return convertedKey;
            }
    
            return prefixCacheKey(convertedKey);
        }
    
        //key处理
        protected String convertKey(Object key) {
            TypeDescriptor source = TypeDescriptor.forObject(key);
            if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
                return conversionService.convert(key, String.class);
            }
    
            Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
    
            if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
                return key.toString();
            }
    
            throw new IllegalStateException(
                    String.format("Cannot convert %s to String. Register a Converter or override toString().", source));
        }
    
        //key处理
        private byte[] createAndConvertCacheKey(Object key) {
            return serializeCacheKey(createCacheKey(key));
        }
    
        //key处理
        private String prefixCacheKey(String key) {
            // allow contextual cache names by computing the key prefix on every call.
            return cacheConfig.getKeyPrefixFor(name) + key;
        }
    View Code

    8、clear()  清除name下所有的key

     //清除name下所有的key
        @Override
        public void clear() {
            byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
            cacheWriter.clean(name, pattern);
        }

      第一步获取key pattern;第二步清除 查看 RedisCacheWriter的子类DefaultRedisCacheWriter的

    void clean(String name, byte[] pattern)

      取出匹配的key,删除掉

        @Override
        public void clean(String name, byte[] pattern) {
    
            Assert.notNull(name, "Name must not be null!");
            Assert.notNull(pattern, "Pattern must not be null!");
    
            execute(name, connection -> {
    
                boolean wasLocked = false;
    
                try {
    
                    if (isLockingCacheWriter()) {
                        doLock(name, connection);
                        wasLocked = true;
                    }
    
                    byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())
                            .toArray(new byte[0][]);
    
                    if (keys.length > 0) {
                        connection.del(keys);
                    }
                } finally {
    
                    if (wasLocked && isLockingCacheWriter()) {
                        doUnlock(name, connection);
                    }
                }
    
                return "OK";
            });
        }
    View Code
  • 相关阅读:
    拿webshell方法汇总
    Linux跑脚本用sh和./有什么区别?
    安全测试工具
    浏览器被劫持网上优秀的修复方法
    Linux 逻辑卷扩容
    sed替换文本
    mysql 用户创建,授权
    编程之约定
    java 对象成员变量初始化顺序
    java 静态成员初始化顺序
  • 原文地址:https://www.cnblogs.com/bjlhx/p/9175408.html
Copyright © 2011-2022 走看看