Spring Boot缓存-原理与实现

Springboot 缓存

1、JSR-107

Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;

1 Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;

2 Cache接口下Spring提供了各种xxxCache的实现,如RedisCache,EhCacheCache ,ConcurrentMapCache等;

3、每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;

4、如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果返回给用户。下次直接从缓存中获取。

5、使用Spring缓存抽象时我们需要关注以下两点:

确定方法需要被缓存以及他们的缓存策略

从缓存中读取之前缓存存储的数据

java Caching 定义了5个核心接口,分别是CachingProviderCacheManagerCacheEntityExpiry

# Caching Provider
1、Caching Provider创建、配置、获取、管理和控制多个CacheManager。
2、一个应用可以在运行期访问多个CachingProvider。
3、redis、memcache、java本身的缓存提供者

# CacheManager
1、CacheManager创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。
2、一个CacheManager仅被一个CachingProvider所拥有。

# Cache
1、Cache是一个类似Map的数据结构并临时存储以Key为索引的值。
2、一个Cache仅被一个CacheManager所拥有。

# Entry
1、Entry是一个存储在Cache中的key-value对.

# Expiry
1、每一个存储在Cache中的条目有一个定义的有效期。
2、一旦超过这个时间,条目为过期的状态。
3、一旦过期,条目将不可访问、更新和删除。
4、缓存有效期可以通过ExpiryPolicy设置。

2、SpringBoot缓存抽象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqe0FPb9-1616692172708)(C:\Users\ThundeRobot\AppData\Roaming\Typora\typora-user-images\image-20210325183316452.png)]

1、一个应用里面可以有多个缓存提供者(CachingProvider)

2、一个缓存提供者可以获取到多个缓存管理器(CacheManager)

3、一个缓存管理器管理着不同的缓存(Cache)

4、缓存中是一个个的缓存键值对(Entry),

5、每个entry都有一个有效期(Expiry)。

6、缓存管理器和缓存之间的关系有点类似于数据库中连接池和连接的关系。

在这里插入图片描述

3、几个重要概念 缓存注解

几个重要概念

img

@CacheConfig
作用:标注在类上,抽取缓存相关注解的公共配置,可抽取的公共配置有缓存名字、主键生成器等(如注解中的属性所示)

@Caching
说明:是@Cacheable、@CachePut、@CacheEvict的组合,定义复杂的缓存规则,在这个组合中只要有@CachePut就一定会调用被注解的方法

属性名描述
cacheNames/value1、指定缓存的名字,缓存使用CacheManager管理多个缓存组件Cache,这些Cache组件就是根据这个名字进行区分的。2、对缓存的真正CRUD操作在Cache中定义,每个缓存组件Cache都有自己唯一的名字,通过cacheNames或者value属性指定。3、相当于是将缓存的键值对进行分组,缓存的名字是一个数组,也就是说可以将一个缓存键值对分到多个组里面
key缓存数据时的key的值,默认是使用方法参数的值,可以使用SpEL表达式计算key的值
keyGenerator缓存的生成策略,和key二选一,都是生成键的,keyGenerator可自定义
cacheManager指定缓存管理器(如ConcurrentHashMap、Redis等)
cacheResolver和cacheManager功能一样,和cacheManager二选一
condition指定缓存的条件(满足什么条件时才缓存),可用SpEL表达式(如#id>0,表示当入参id大于0时才缓存)
unless否定缓存,即满足unless指定的条件时,方法的结果不进行缓存,使用unless时可以在调用的方法获取到结果之后再进行判断(如#result==null,表示如果结果为null时不缓存)
sync是否使用异步模式进行缓存

注:
1、既满足condition又满足unless条件的也不进行缓存
2、使用异步模式进行缓存时(sync=true):unless条件将不被支持

img

img

4、缓存使用

package com.realguo.cache01.service;

import com.realguo.cache01.mapper.UserMapper;
import com.realguo.cache01.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "user")
public class UserService {
    @Autowired
    UserMapper userMapper;

    /**
     * 自动缓存
     * @param username
     * @return
     */
    @Cacheable(cacheNames = "user", key = "#username")
    public User getUserByUsername(String username){
        System.out.println("查询 username=" + username + "的员工");
        return userMapper.getUserByUsername(username);
    }

    /**
     * 先调用方法,然后把方法结果放在缓存中
     * @param user
     * @return
     */
    @CachePut(key = "#result.username")
    public User updateUserByUsername(User user){
        System.out.println("updata username:" + user.getUsername());
        userMapper.updateUserByUsername(user);
        return user;
    }

    /**
     * beforeInvocation = true  调用方法前,清除缓存
     * beforeInvocation = false 调用方法后,清除缓存,方法失败,则清除缓存失败
     * allEntries = true        清除所有缓存
     *
     * @param username
     */
    @CacheEvict(beforeInvocation = false)
    public void deleteUser(String username){
        System.out.println("delete " + username);
        userMapper.deleteUserByUsername(username);
    }
}

@Cacheables能够根据方法的请求参数对其结果进行缓存

cacheNames 缓存的名称:也就是在缓存中有一个叫emp的名字来标识不同的缓存组件

key表示entity中的key

5、SpringBoot缓存原理

<!--回顾spring基于注解开发-->
<context:component-scan base-package="com.realguo"/>
<context:annotation-config/>

两个配置类融合:@Import(config2.class)

@Configuration 完全使用java的方式进行配置,不用spring的xml配置

JavaConfig是一个Spring 的一个子项目,在Spring4之后,称为一个核心功能。

1、一个自动装配类:CacheAutoConfiguration

2、导入CacheConfigImportSelector.class配置类,它向数组import中添加从CacheConfigurations获得的配置类名

@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class, CacheAutoConfiguration.CacheManagerEntityManagerFactoryDependsOnPostProcessor.class})

for(int i = 0; i < types.length; ++i) {
    imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}

3、CacheConfigurations这里类里面有一个map,里面的key是类名的缩写,value是缓存配置类

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

4、默认生效的配置类:SimpleCacheConfiguration

# spring boot中查看默认生效的配置类
# 在application.properties中配置
debug=true

# 结果
Positive matches:     表示自动配置类启用的
Negative matches:     不生效的
Exclusions:           手动设定不自动装配的
Unconditional classes: 不生效的

5、SimpleCacheConfiguration给容器中注册了一个CacheManager:ConcurrentMapCacheManager

@Bean
ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers) {
    ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
    List<String> cacheNames = cacheProperties.getCacheNames();// 到properties.class中取配置
    if (!cacheNames.isEmpty()) {
        cacheManager.setCacheNames(cacheNames);
    }

    return (ConcurrentMapCacheManager)cacheManagerCustomizers.customize(cacheManager);
}

6、通过ConcurrentMapCacheManager中有ConcurrentMap,用来存Cache

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
    private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);
}

7、Cache是ConcurrentMapCache

public class ConcurrentMapCache extends AbstractValueAdaptingCache {
    private final String name;
    private final ConcurrentMap<Object, Object> store;
}

在这里插入图片描述

@Cacheable运行流程

1、方法执行前,查询Cache(缓存组件),按照CacheNames从CacheManager那获得缓存组件,如果不存在,会自动创建Cache(缓存组件)

2、从缓存组件Cache中查询缓存的内容,使用key查询

3、key是使用keyGenerator生成的,默认使用的是SimpleKeyGenerator

# SimpleKeyGenerator生成key的默认策略:
1、如果没有参数:key = new SimpleKey();
2、如果有一个参数:key = 参数的值
3、如果有多个参数:key = new SimpleKey(params)

4、没有查到缓存就调用目标方法

5、将目标方法返回的结果放进缓存中

核心:
1、使用CacheManager(ConcurrentMapCacheManager)按照名字得到Cache(ConcurrentMapCache)组件
2、key使用keyGenerator生成,默认使用SimpleKeyGenerator

6、整合redis

Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、 有序集合(sorted sets)与范围查询、bitmaps、hyperloglogs和地理空间(geospatial)索引半径查询。Redis内置了复制(replication),LUA脚本(Lua scripting)、 LRU驱动事件(LRU eviction),事务(transactions)和不同级别的磁盘持久化(persistence),并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。

感觉又回到了原点。。。。

1、reidsCacheConfiguration里面有redisCacheManager

2、所以我们要重写自己的redisCacheManager,然后指定用自己的redisCacheManager

3、然而看了博客,也就是在redisCacheManager中使用redisTemplate罢了

4、所以本质上,还是在用redisTemplate

5、当然使用缓存的注解的使用,也可以指定使用redisCacheManager,但是好像这种方式不流行

参考

SpringBoot高级——缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值