Spring Cache简化缓存开发

Posted 赵广陆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cache简化缓存开发相关的知识,希望对你有一定的参考价值。

目录


1 背景介绍

官方入门文档:https://spring.io/guides/gs/caching/

Spring 从3.1 开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用JCache(JSR-107)注解简化我们开发;Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合; Cache 接口下Spring 提供了各种xxxCache 的实现;如RedisCache , EhCacheCache , ConcurrentMapCache 等;
每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已 经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

如果我们的程序想要使用缓存,就要与这些框架耦合。聪明的架构师已经在利用接口来降低耦合了,利用面向对象的抽象和多态的特性,做到业务代码与具体的框架分离。
但我们仍然需要显式地在代码中去调用与缓存有关的接口和方法,在合适的时候插入数据到缓存里,在合适的时候从缓存中读取数据。
想一想AOP的适用场景,这不就是天生就应该AOP去做的吗?
是的,Spring Cache就是一个这个框架。它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,用户可以3秒钟就使用上一个很不错的缓存功能。
既然有这么好的轮子,干嘛不用呢?

2 使用方式

Spring cache提供了开箱即用的接入方式,只需要若干注解和缓存管理类即可接入.

1.开启缓存能力
引入缓存依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

在应用启动类添加@EnableCaching注解:

@SpringBootApplication
@EnableCaching
public class Application 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

2.使用缓存
在业务方法添加@Cacheable注解:

@Cacheable(cacheNames = "task")
public TaskInfoDTO getTask(String taskId) 
    log.info("TestBuzz.getTask mock query from DB......");
    TaskInfoDTO taskInfoDTO = new TaskInfoDTO();
    taskInfoDTO.setTaskId(taskId);
    taskInfoDTO.setApplicantId("system");
    taskInfoDTO.setDescription("test");
    return taskInfoDTO;

模拟请求:

@GetMapping("/test_cache")
public IResp<TaskInfoDTO> testCache() 
    TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123");
    return IResp.getSuccessResult(taskInfoDTO);

连续发送两次查询请求:

curl http://localhost:port/test_cache


从日志中看到只打印了一次DB调用,也就是说明第二次走了缓存。就这么简单我们就开启并使用了spring的缓存能力。

3 常用注解

原理:基于Proxy/AspectJ动态代理技术的AOP思想(面向切面编程)
使用

  1. SpringCache包含两个顶级接口,Cache(缓存)和CacheManager(缓存管理器),顾名思义,用CacheManager去管理一堆Cache。
  2. spring cache实现有基于XML/注解实现AOP;
  3. CacheManager负责对缓存的增删改查, CacheManager的缓存的介质可配置, 如:ConcurrentMap/EhCache/Redis等。当没有加入EhCache或者Redis依赖时默认采用concurrentMap实现的缓存,是存在内存中,重启服务器则清空缓存

pring Cache中的注解主要有如下五个:

  • @Cacheable:缓存数据或者获取缓存数据
  • @CachePut:修改缓存数据
  • @CacheEvict: 清空缓存
  • @CacheConfig:统一配置@Cacheable中的value值
  • @Caching:组合多个Cache注解

3.1 @Cacheable

先从value中获取为key的缓存,如果存在直接返回;如果不存在则执行方法并返回,且把返回输出存入缓存。(注意:保存的数据是return返回的数据)
主要有三个参数:

  • value:缓存的名称,可以多个 (必填,也可以用@CacheConfig替代)
 @Cacheable(value="testcache")
 @Cacheable(value="testcache1","testcache2"
  • key:缓存的 key,按照 SpEL 表达式编写,为空时默认为方法的入参(value相当于缓存空间的名称,而key相当于是一个缓存值的名字)
@Cacheable(value="testcache",key="#id")
  • condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false
@Cacheable(value="testcache",condition="#id.length()>2")

3.2 @CachePut

根据value中获取为key的缓存,如果存在则修改;不存在则新增
主要是三个参数
value,key,condition如上所示。

注意:保存的数据是return返回的数据,如下返回的有user对象和null,第一个缓存的数据是实体类,第二个缓存的数据是空

3.3 @CacheEvict

根据对应的value和key删除缓存,没有key值则删除value中所有的缓存
主要有五个参数value,key,condition,allEntries,beforeInvocation

  • allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
@CachEvict(value="testcache",allEntries=true)

图一、会清空getData下面所有缓存(allEntries=true则删除所有)

图二、只会清空getData下面key值为id的缓存(没有key默认取入参)

图三、不会清空任何缓存

  • beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为
    true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存(注:作用只有一个,就是先清空缓存再执行方法
@CachEvict(value="testcache",beforeInvocation=true)

3.4 @CacheConfig

相当于把类下面所有方法@Cacheable中的value值放到@CacheConfig注解中,
如果@Cacheable中没有value值则用@Cacheable中的值;如果@Cacheable中有value值则以value值为准。

@CacheConfig("testcache")
public class UserServiceImpl implements  UserService

    @Cacheable
    public Result findById(Long id) 
    
    
	@Cacheable
    public Result findByIdAndName(Long id,String name) 
    

3.5 @Caching

组合注解,可以组合多个注解

@Caching(put = 
	@CachePut(value = "user", key = "#user.id"),
	@CachePut(value = "user", key = "#user.username"),
	@CachePut(value = "user", key = "#user.email")
)
public User save(User user) 

4 实战:Spring Cache整合redis缓存

4.1 CacheManager 缓存管理器

public interface CacheManager 

	//根据缓存名字获取缓存
	@Nullable
	Cache getCache(String name);

	//获取管理的所有缓存的名字
	Collection<String> getCacheNames();


支持多种类型的缓存

这里我们使用redis作为缓存

<!--    引入redis    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--   jedis不写版本springboot控制     -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>

4.2 配置spring-boot-starter-cache

1.缓存的自动配置
CacheProperties封装了配置文件中可以配置的属性

@ConfigurationProperties(prefix = "spring.cache")
org.springframework.boot.autoconfigure.cache.CacheProperties
CacheConfiguration`会根据缓存类型导入`RedisCacheConfiguration

RedisCacheConfiguration自动配好了缓存管理器

@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());
	

4.3 手动配置

配置使用Redis作为缓存

  spring:
  	cache:
      #cache-names: 可以自动配置
      type: redis

4.4 开启缓存功能 @Cacheable(“…”)

在启动类添加@EnableCaching 注解,不需要重复配Redis

@EnableCaching 注解

2.使用注解 就能完成缓存操作

//每一个需要缓存的数据都需要指定要放到哪个名字的缓存,缓存的分区,按照业务类型分 
 @Cacheable("categroy")    //代表方法的结果需要缓存 ,如果缓存中有 方法都不用调用,如果缓存中没有,调用方法,将结果放入缓存
 @Override
 public List<CategoryEntity> getLevel1Categorys() 
     long l = System.currentTimeMillis();
     List<CategoryEntity> categoryEntityList = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
     System.out.println("消耗时间:" + (System.currentTimeMillis() - l));
     return categoryEntityList;
 

使用@Cacheable 默认行为
如果缓存中有缓存 方法都不用调用
key值 自动生成
缓存的Value值默认使用JDK序列化机制,将序列化后的数据存到Redis
默认过期时间TTL -1 永不过去

应该要指定缓存存活时间 在配置文件中修改

  cache:
    #cache-names: 可以自动配置
    type: redis
    redis:
      time-to-live: 3600000  #设置存活时间毫秒
      key-prefix: CACHE_    #key前缀 如果制定了前缀就用指定的前缀,如果没有就默认使用缓存的名字作为前缀
      use-key-prefix: true  # 是否使用前缀
      cache-null-values: true # 是否缓存控制 解决缓存穿透

以上是关于Spring Cache简化缓存开发的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot整合Spring Cache,简化分布式缓存开发

SpringBoot整合Spring Cache,简化分布式缓存开发

SpringBoot整合Spring Cache,简化分布式缓存开发

SpringBoot整合Spring Cache,简化分布式缓存开发

缓存抽象层Spring cache实战操作

史上最全的Spring Boot Cache使用与整合