SpringBoot缓存

spring定义了CacheCacheManager两个接口来统一管理不同的缓存技术,同时也支持使用JCache(JSR-107)注解来简化我们开发

  • Cache:缓存接口,定义缓存操作,实现有:RedisCache,EhCacheCache,ConcurrentMapCache等
  • CacheManager:缓存管理器,管理各种缓存(Cache)组件
几个常见的注解

缓存注解

解释

@Cacheable

主要配置在方法上,根据方法的请求参数对其结果进行缓存

@CacheEvict

清空缓存

@CachePut

方法被调用的同时,将结果缓存

@EnableCaching

开启基于注解的缓存

SpringBoot缓存的运行原理

缓存的自动配置类CacheAutoConfiguration导入了CacheConfigurationImportSelector

springboot redis宕机时接口查询数据库_redis


其为我们导入了十个缓存配置类

springboot redis宕机时接口查询数据库_spring_02


默认生效的是SimpleCacheConfiguration

springboot redis宕机时接口查询数据库_redis_03


其给容器中注册了一个CacheManager:ConcurrentMapCacheManager

获取和创建ConcurrentMapCacheManager类型的缓存组件,将数据保存在ConcurrentMap

springboot redis宕机时接口查询数据库_spring_04

运行流程:

1.方法运行之前,先去查询cache缓存组件,按照cacheName指定的名字获取,第一次获取缓存如果没有cache组件会自动创建出来

public Cache getCache(String name) {
   	Cache cache = this.cacheMap.get(name);
   	if (cache == null && this.dynamic) {
   		synchronized (this.cacheMap) {
   			cache = this.cacheMap.get(name);
   			if (cache == null) {
   				cache = createConcurrentMapCache(name);
   				this.cacheMap.put(name, cache);
   			}
   		}
   	}
   	return cache;
   }

2.去cache中按照key查找缓存的内容,key默认就是方法的参数

protected Object lookup(Object key) {
   	return this.store.get(key);
   }

3.没有查找到缓存就调用目标方法,也就是去查找数据库
4.将找到的结果放进缓存中

public void put(Object key, @Nullable Object value) {
		this.store.put(key, toStoreValue(value));
	}

@Cacheable注解总的来说就是其标注的方法执行之前先来检查缓存中有没有数据,没有就去数据库中查并将结果放进缓存

缓存实例

1.开启基于注解的缓存

@SpringBootApplication
@EnableCaching//开启基于注解的缓存
@MapperScan(value = "com.lb.springboot.mapper")
public class SpringbootCacheApplication {

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

}

2.在方法上标注@Cacheable并设置缓存名

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    @Cacheable(value = "getEmp")
    public Employee getEmp(int id){
        System.out.println("查询用户ID:"+id);
        return employeeMapper.getEmp(id);
    }
}

3.效果展示

@Cacheable

第一次获取员工信息时,因为没有缓存在没有该信息,所以会执行方法去数据库查找,并将结果放进缓存中

springboot redis宕机时接口查询数据库_spring_05


第二次获取时,因为缓存已经有该信息,所以就不需要执行方法去数据库查找而是直接从缓存中获取

springboot redis宕机时接口查询数据库_缓存_06


@CachePut

在方法上标注@CachePut并设置缓存名

@CachePut(value = "emp",key = "#result.id")
    public Employee updateEmp(Employee employee){
        System.out.println("更新用户ID:"+employee.getId());
        employeeMapper.updateEmp(employee);
        return employee;
    }

效果

每次调用方法都会执行

springboot redis宕机时接口查询数据库_redis_07


并且将更新后的数据保存在缓存中

springboot redis宕机时接口查询数据库_spring_08


CacheEvict

在方法上标注@CacheEvict并设置要删除的key

@CacheEvict(value = "emp",key = "#id")
    public void deleteEmp(int id){
        System.out.println("deleteEmp: "+id);
    }

效果

会把缓存中对应的key删除

springboot redis宕机时接口查询数据库_spring_09


随后获取数据时,因为缓存中该数据被删除了,所以需要重新调用方法去数据库查询

springboot redis宕机时接口查询数据库_缓存_10

springboot整合redis

导入redis依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

启动redis服务和连接redis

springboot redis宕机时接口查询数据库_redis_11

springboot redis宕机时接口查询数据库_redis_12


springboot中redis操作主要有两大类

  • RedisTemplate<Object,Object>
  • StringRedisTemplate<String,String>

StringRedisTemplate继承RedisTemplate

redis支持String,Hash,Set,List,Zset五大类型,这里只测试List和String,其他类型的方法都是大同小异

public class SpringbootRedisApplicationTests {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Test
    public void contextLoads() {
        stringRedisTemplate.opsForValue().set("msg","redis" );
        System.out.println(stringRedisTemplate.opsForValue().get("msg"));

springboot redis宕机时接口查询数据库_spring_13

public class SpringbootRedisApplicationTests {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Test
    public void contextLoads() {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        stringRedisTemplate.opsForList().leftPushAll("list",list );
        System.out.println(stringRedisTemplate.opsForList().index("list",2 ));
    }

springboot redis宕机时接口查询数据库_缓存_14


在请求中使用redis

@RestController
public class cacheController {
    @Autowired
    EmployeeService employeeService;

    @GetMapping("/getEmp/{id}")
    public Employee get(@PathVariable("id") int id){
        Employee employee = employeeService.getEmp(id);
        return employee;
    }
}

注意把对象存进redis中时需要序列化

springboot redis宕机时接口查询数据库_redis_15


请求

@RestController
public class cacheController {
    @Autowired
    EmployeeService employeeService;

    @GetMapping("/getEmp/{id}")
    public Employee get(@PathVariable("id") int id){
        Employee employee = employeeService.getEmp(id);
        return employee;
    }
}

springboot redis宕机时接口查询数据库_spring_16


springboot redis宕机时接口查询数据库_缓存_17

springboot redis宕机时接口查询数据库_redis_18

完整代码在我的GitHub