SpringBoot Cache缓存概念讲解

 更新时间:2022年12月30日 14:27:42   作者:wenxueliu@HW  
这篇文章主要介绍了Springboot cache缓存,使用缓存最关键的一点就是保证缓存与数据库的数据一致性,本文给大家介绍最常用的缓存操作模式,对Springboot cache缓存操作流程感兴趣的朋友一起看看吧

Spring 3.1中引入了基于注解的Cache的支持,在spring-context包中定义了org.springframework.cache.CacheManager和org.springframework.cache.Cache接口,用来统一不同的缓存的技术。

CacheManager是Spring提供的各种缓存技术管理的抽象接口,而Cache接口包含缓存的增加、删除、读取等常用操作。针对CacheManager,Spring又提供了多种实现,比如基于Collection来实现的SimpleCacheManager、基于ConcurrentHashMap实现的ConcurrentMapCacheManager、基于EhCache实现的EhCacheCacheManager和基于JCache标准实现的JCacheCacheManager等。

Spring Cache提供了@CacheConfig、@Cacheable、@CachePut、@CacheEvict等注解来完成缓存的透明化操作,相关功能如下。

  • @CacheConfig:用于类上,缓存一些公共设置。
  • @Cacheable:用于方法上,根据方法的请求参数对结果进行缓存,下次读取时直接读取缓存内容。
  • @CachePut:用于方法上,能够根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法的调用
  • @CacheEvict:用于方法上,清除该方法的缓存,用在类上清除整个类的方法的缓存。在了解了Spring Cache的基本作用的和定义之后,下面来看在Spring Boot中是如何对Cache进行自动配置的

Cache自动配置

在Spring Boot中,关于Cache的默认自动配置类只有CacheAutoConfiguration,主要用于缓存抽象的自动配置,当通过@EnableCaching启用缓存机制时,根据情况可创建CacheManager。对于缓存存储可以通过配置自动检测或明确指定。CacheAutoConfiguration同样在META-INF/spring.factories文件中配置注册。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

下面先来看CacheAutoConfiguration类的注解部分代码实现。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
    HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration { 
...
}

1、@ConditionalOnClass 指定需要在classpath下存在CacheManager类。

2、@ConditionalOnBean 指定需要存在CacheAspectSupport的Bean时才生效,换句话说,就是需要在使用了@EnableCaching时才有效。这是因为该注解隐式的导致了CacheInterceptor对应的Bean的初始化,而CacheInterceptor为CacheAspectSupport的子类。

3、@ConditionalOnMissingBean指定名称为cacheResolver的CacheManager对象不存在时生效。@EnableConfigurationProperties加载缓存的CacheProperties配置项,配置前缀为spring.cache。

4、@EnableConfigurationProperties加载缓存的CacheProperties配置项,配置前缀为spring.cache。@AutoConfigureAfter指定该自动配置必须在缓存数据基础组件自动配置之后进行,这里包括Couchbase、Hazelcast、HibernateJpa和Redis的自动配置。

5、@Import导入CacheConfigurationImportSelector,其实是导入符合条件的Spring Cache使用的各类基础缓存框架(或组件)的配置。

ImportSelector

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++) {
            imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
        }
        return imports;
    }
}

导入类的获取是通过实现ImportSelector接口来完成的,具体获取步骤位于selectImports方法中。该方法中,首先获取枚举类CacheType中定义的缓存类型数据,CacheType中定义支持的缓存类型如下。

// 支持的缓存类型(按照优先级定义)
public enum CacheType {
    // 使用上下文中的Cache Bean进行通用缓存
    GENERIC,
    // JCache(JSR-107)支持的缓存
    JCACHE,
    // EhCache支持的缓存
    EHCACHE,
    // Hazelcast支持的缓存
    HAZELCAST,
    // Infinispan支持的缓存
    INFINISPAN,
    // Couchbase支持的缓存
    COUCHBASE,
    // Redis支持的缓存
    REDIS,
    // Caffeine支持的缓存
    CAFFEINE,
    // 内存基本的简单缓存
    SIMPLE,
    // 不支持缓存
    NONE
}

枚举类CacheType中定义了以上支持的缓存类型,而且上面的缓存类型默认是按照优先级从前到后的顺序排列的。selectImports方法中,当获取CacheType中定义的缓存类型数组之后,遍历该数组并通过CacheConfigurations的getConfigurationClass方法获得每种类型缓存对应的自动配置类(注解@Configuration的类)。CacheConfigurations相关代码如下。

final class CacheConfigurations {
    private static final Map<CacheType, Class<?>> MAPPINGS;
    // 定义CacheType与@Configuration之间的对应关系
    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);
    }
    // 根据CacheType类型获得对应的@Configuration类
    static String getConfigurationClass(CacheType cacheType) {
        Class<?> configurationClass = MAPPINGS.get(cacheType);
        Assert.state(configurationClass != null, () -> "Unknown cache type " + cacheType);
        return configurationClass.getName();
    }
    ...
}

我们会发现通过@Import注解,CacheAutoConfiguration导入了CacheType中定义的所有类型的自动配置,也就是Spring Boot目前支持的缓存类型。而具体会自动配置哪种类型的缓存,还需要看导入的自动配置类里面的生效条件。

GenericCacheConfiguration

// 实例化CacheManagerCustomizers
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
        ObjectProvider<CacheManagerCustomizer<?>> customizers) {
    return new CacheManagerCustomizers(
            customizers.orderedStream().collect(Collectors.toList()));
}

cacheManagerCustomizers方法初始化了CacheManagerCustomizers对象的Bean,主要是将容器中存在的一个或多个CacheManagerCustomizer的Bean组件包装为CacheManager-Customizers,并将Bean注入容器。

CacheManagerValidator

cacheAutoConfigurationValidator方法初始化了CacheManagerValidator的Bean,该Bean用于确保容器中存在一个CacheManager对象,以达到缓存机制可以继续被配置和使用的目的,同时该Bean也用来提供有意义的异常声明。

// 实例化CacheManagerValidator
@Bean
public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties-
cacheProperties,
        ObjectProvider<CacheManager> cacheManager) {
    return new CacheManagerValidator(cacheProperties, cacheManager);
}
// CacheManagerValidator的具体定义,用于检查并抛出有意义的异常
static class CacheManagerValidator implements InitializingBean {
    private final CacheProperties cacheProperties;
    private final ObjectProvider<CacheManager> cacheManager;
    CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<Cache-
Manager> 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() + "')");
    }
}

Redis Cache

RedisCacheConfiguration

我们以RedisCacheConfiguration为例进行分析。源码如下

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
	@Bean
	RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
			ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
			ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
			RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
		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));
		return cacheManagerCustomizers.customize(builder.build());
	}
	private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
			CacheProperties cacheProperties,
			ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
			ClassLoader classLoader) {
		return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));
	}
	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();
		config = config.serializeValuesWith(
				SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
		if (redisProperties.getTimeToLive() != null) {
			config = config.entryTtl(redisProperties.getTimeToLive());
		}
		if (redisProperties.getKeyPrefix() != null) {
			config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
		}
		if (!redisProperties.isCacheNullValues()) {
			config = config.disableCachingNullValues();
		}
		if (!redisProperties.isUseKeyPrefix()) {
			config = config.disableKeyPrefix();
		}
		return config;
	}
}

@ConditionalOnClass:当 RedisConnectionFactory 在存在时

@AutoConfigureAfter:在 RedisAutoConfiguration 之后加载

@ConditionalOnBean:RedisConnectionFactory 实例化之后

@ConditionalOnMissingBean:当 CacheManager 的 Bean 不存在时进行实例化操作

@Conditional:满足 CacheCondition 条件时,进行实例化操作

CacheCondition

class CacheCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, Annotated-
TypeMetadata metadata) {
        String sourceClass = "";
        if (metadata instanceof ClassMetadata) {
            sourceClass = ((ClassMetadata) metadata).getClassName();
        }
        ConditionMessage.Builder message = ConditionMessage.forCondition("Cache", 
sourceClass);
        Environment environment = context.getEnvironment();
        try {
            // 创建指定环境的Binder,然后绑定属性到对象上
            BindResult<CacheType> specified = Binder.get(environment).bind("spring.
cache.type", CacheType.class);
            // 如果未绑定,则返回匹配
            if (!specified.isBound()) {
                return ConditionOutcome.match(message.because("automatic cache type"));
            }
            // 获取所需的缓存类型
            CacheType required = CacheConfigurations.getType(((AnnotationMetadata) 
metadata).getClassName());
            // 如果已绑定,并且绑定的类型与所需的缓存类型相同,则返回匹配
            if (specified.get() == required) {
                return ConditionOutcome.match(message.because(specified.get() + " cache type"));
            }
        } catch (BindException ex) {
        }
        // 其他情况则返回不匹配
        return ConditionOutcome.noMatch(message.because("unknown cache type"));
    }
}

CacheCondition的核心逻辑就是首先通过Binder进行指定属性和类的绑定,然后通过绑定结果(BindResult)进行判断:如果判断结果是未绑定,则直接返回条件匹配;否则,判断绑定的缓存类型与所需的缓存类型是否相等,如果相等则返回条件匹配;其他情况则返回条件不匹配。

RedisCacheManager

1、管理多个 RedisCache

2、每个 RedisCache 包含一个 name

3、每个 RedisCache 可以包含一个 RedisCacheConfiguration(可以有默认配置)

4、支持配置是否动态增加 Cache

public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
	private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
	private volatile Set<String> cacheNames = Collections.emptySet();
}
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
    private final RedisCacheWriter cacheWriter;
    private final RedisCacheConfiguration defaultCacheConfig;
    private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
    private final boolean allowInFlightCacheCreation;
    protected Collection<RedisCache> loadCaches() {
        List<RedisCache> caches = new LinkedList();
        Iterator var2 = this.initialCacheConfiguration.entrySet().iterator();
        while(var2.hasNext()) {
            Entry<String, RedisCacheConfiguration> entry = (Entry)var2.next();
            caches.add(this.createRedisCache((String)entry.getKey(), (RedisCacheConfiguration)entry.getValue()));
        }
        return caches;
    }
    protected RedisCache getMissingCache(String name) {
        return this.allowInFlightCacheCreation ? this.createRedisCache(name, this.defaultCacheConfig) : null;
    }
    public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
        Map<String, RedisCacheConfiguration> configurationMap = new HashMap(this.getCacheNames().size());
        this.getCacheNames().forEach((it) -> {
            RedisCache cache = (RedisCache)RedisCache.class.cast(this.lookupCache(it));
            configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
        });
        return Collections.unmodifiableMap(configurationMap);
    }
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        return new RedisCache(name, this.cacheWriter, cacheConfig != null ? cacheConfig : this.defaultCacheConfig);
    }
}

RedisCache

public class RedisCache extends AbstractValueAdaptingCache {
    private static final byte[] BINARY_NULL_VALUE;
    private final String name;
    private final RedisCacheWriter cacheWriter;
    private final RedisCacheConfiguration cacheConfig;
    private final ConversionService conversionService;
}

RedisCache 的特点

1、key 支持非 String 类型,比如 Map 和 Collection

2、RedisCacheWriter 是更底层的实现,RedisCache 对 RedisCacheWriter 进行封装。

3、通过 ConversionService 对 key 进行转换

RedisCacheWriter

通过 RedisConnectionFactory 取一个 RedisConnection,然后执行命令。putIfAbsent 支持分布式锁。

到此这篇关于SpringBoot Cache缓存概念讲解的文章就介绍到这了,更多相关SpringBoot Cache内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java hashCode原理以及与equals()区别联系详解

    Java hashCode原理以及与equals()区别联系详解

    在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致
    2022-11-11
  • java实现单词小游戏

    java实现单词小游戏

    这篇文章主要为大家详细介绍了java实现单词小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-02-02
  • Java生成CSV文件实例详解

    Java生成CSV文件实例详解

    这篇文章主要介绍了Java生成CSV文件的方法,很实用的功能,需要的朋友可以参考下
    2014-07-07
  • SpringBoot在idea中的 .idea和 .iml文件的作用

    SpringBoot在idea中的 .idea和 .iml文件的作用

    本文主要介绍了SpringBoot在idea中的 .idea和 .iml文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • java stringbuffer的用法示例

    java stringbuffer的用法示例

    这篇文章主要介绍了java stringbuffer的用法示例,字符串缓冲区,是一个容器(当返回到的是String时而且长度不确定,数据类型不确定时就可以用StringBuffer)其实底层还是数组,只是被封装了,对外提供了方法,初始容量为16个字符
    2014-01-01
  • Java中Lambda表达式并行与组合行为

    Java中Lambda表达式并行与组合行为

    这篇文章主要介绍了Java中Lambda表达式并行与组合行为,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-02-02
  • VS Code中运行Java SpringBoot的项目详细步骤

    VS Code中运行Java SpringBoot的项目详细步骤

    这篇文章主要介绍了VS Code中运行Java SpringBoot项目的相关资料,文中涵盖了安装必要的扩展、配置环境、创建或导入项目、配置调试环境、运行和调试项目、使用Spring Boot Actuator以及配置任务自动化等步骤,需要的朋友可以参考下
    2024-12-12
  • 浅析JVM逃逸的原理及分析

    浅析JVM逃逸的原理及分析

    在本篇文章里我们给大家分享了JVM逃逸的原理及分析的相关知识点内容,需要的读者们可以学习下。
    2018-10-10
  • Java结构型设计模式中建造者模式示例详解

    Java结构型设计模式中建造者模式示例详解

    建造者模式,是一种对象构建模式 它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。本文将通过示例讲解建造者模式,需要的可以参考一下
    2022-09-09
  • java外部类与内部类简介

    java外部类与内部类简介

    这篇文章简单介绍了java外部类与内部类,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12

最新评论