Spring Boot缓存

1 Spring 缓存抽象

image.png
  • @Cacheable:对请求参数和结果缓存,下次用同一个参数请求,就不再调用方法,直接从缓存中拿出数据
  • @CacheEvict:清空缓存
  • @CachePut:更新缓存,保证方法一定会被调用,同时更新缓存中的对应的数据。
  • @EnableCaching:开启缓存的注解,开启了才可以使用缓存

2 简单使用

在启动类上面加注解@EnableCaching,开启缓存

@EnableCaching
public class Springboot01CacheApplication {

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

在调用数据库的方法上面使用@Cacheable

    //将数据缓存在一个叫emp的缓存中去,下次查询同一个id的时候,就不会重新查询数据库了
    @Cacheable(value = {"emp"})
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

3 @Cacheable的属性

3.1 cacheNames/value

指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;

    @Cacheable(value = {"emp"})
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

3.2 key

缓存是以key-value形式保存的,默认使用的key是方法参数的值,value是方法的返回值。
可以使用spel的方式取出一些值来作为key:



比如:取出第一个方法的参数作为key

    @Cacheable(value = {"emp"},key = "#root.args[0]")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

3.3 keyGenerator

key的生成器;可以自己指定key的生成器的组件id
key/keyGenerator:二选一使用;

    @Cacheable(value = {"emp"},keyGenerator = "myKeyGenerator")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){

            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString()+"]";
            }
        };
    }
}

3.4 cacheManager/cacheResolver

指定缓存管理器/指定获取解析器,二者作用相同

3.5 condition

指定符合条件的情况下才缓存
比如:condition = "#a0>1 and #root.methodName eq 'aaa' ":第一个参数的值>1并且方法名是aaa的时候才进行缓存

3.6 unless

当unless指定的条件为true,方法的返回值就不会被缓存
比如:
unless = "#result == null",如果方法的返回值为空,不缓存
unless = "#a0==2":如果第一个参数的值是2,结果不缓存;

3.7 sync

是否使用异步模式,默认false,改成true时,unless不可用

4 缓存的原理

4.1 自动配置类

CacheAutoConfiguration

4.2 缓存的配置类

/*
     *   org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
     *   org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
*/

SimpleCacheConfiguration 给容器中注册了一个CacheManager:ConcurrentMapCacheManager,可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中

4.3 @Cacheable运行流程

  • 根据cacheNames指定的名字获取Cache(缓存组件),第一次获取缓存如果没有Cache组件会自动创建。
  • 根据key去chche中查找值,如果没有就执行方法,并将方法执行的结果放进缓存中。之后再调用直接使用缓存中的数据

4.4 key的生成策略

key是可以指定的,不指定的话,有一个默认的key生成策略SimpleKeyGenerator
SimpleKeyGenerator生成key的默认策略:

  • 如果没有参数:key=new SimpleKey();
  • 如果有一个参数:key=参数的值
  • 如果有多个参数:key=new SimpleKey(params);

5 @CachePut

既调用方法,又更新缓存

    //因为是先调用方法,后更新缓存,所以可以使用#result.id,
    //@Cacheable的key则不可以使用#result.id,因为@Cacheable是要先查缓存的
    @CachePut(value = "emp",key = "#result.id")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

6 @CacheEvict

  • key:指定要清除的数据
  • allEntries = true:指定清除这个缓存中所有的数据
  • beforeInvocation = false:缓存的清除是否在方法之前执行
    默认false代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
    //清除emp里面对应key=当前id的缓存
    @CacheEvict(value="emp",key = "#id")
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        employeeMapper.deleteEmpById(id);
    }
    //清除emp里面所有的缓存
    @CacheEvict(value="emp",allEntries = true)
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        employeeMapper.deleteEmpById(id);
    }
    //清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除
    @CacheEvict(value="emp",beforeInvocation = true)
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        employeeMapper.deleteEmpById(id);
        int i = 10/0;
    }

7 @Caching

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
    Cacheable[] cacheable() default {};

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
}
    // @Caching 定义复杂的缓存规则
    @Caching(
         cacheable = {
             @Cacheable(value="emp",key = "#lastName")
         },
         put = {
             @CachePut(value="emp",key = "#result.id"),
             @CachePut(value="emp",key = "#result.email")
         }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

8 @CacheConfig

//写在类上面
@CacheConfig(cacheNames="emp") //抽取缓存的公共配置


以上是默认的使用ConcurrentMapCacheManager,数据保存在ConcurrentMap,接下来使用别的缓存

1 使用redis缓存

2 简单使用redis

2.1导包

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

2.2 配置

spring.redis.host=192.168.60.137

2.3 使用

    @Autowired
    StringRedisTemplate stringRedisTemplate;  //操作k-v都是字符串的

    @Autowired
    RedisTemplate redisTemplate;  //k-v都是对象的

        Redis常见的五大数据类型
     tring(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
     stringRedisTemplate.opsForValue()[String(字符串)]
     stringRedisTemplate.opsForList()[List(列表)]
     stringRedisTemplate.opsForSet()[Set(集合)]
     stringRedisTemplate.opsForHash()[Hash(散列)]
     stringRedisTemplate.opsForZSet()[ZSet(有序集合)]

      比如:
    stringRedisTemplate.opsForValue().append("msg","hello");
        String msg = stringRedisTemplate.opsForValue().get("msg");

        stringRedisTemplate.opsForList().leftPush("mylist","1");
        stringRedisTemplate.opsForList().leftPush("mylist","2");

redis保存对象

首先对象要implements Serializable

  • 可以自己将对象转换为json,再像保存string一样保存
  • 或者改变默认的序列化规则
@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer ser = new Jackson2JsonRedisSerializer(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }

使用:


    @Autowired
    RedisTemplate empRedisTemplate;

    @Test
    public void test02(){
        Employee empById = employeeMapper.getEmpById(1);
        empRedisTemplate.opsForValue().set("emp-01",empById);
    }

3 使用redis作为缓存

  • 引入redis的starter,容器中保存的是 RedisCacheManager;默认的ConcurrentMapCacheManager就不再起作用了。
  • RedisCacheManager 帮我们创建 RedisCache 来作为缓存组件;RedisCache通过操作redis缓存数据

其实就可以直接使用了

有问题:保存对象时,保存的是序列化后的内容,希望它保存的是json的内容:
解决:

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer ser = new Jackson2JsonRedisSerializer(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }

    @Bean
    public RedisCacheManager employeeCacheManager(RedisTemplate empRedisTemplate){
        RedisCacheManager cacheManager = new RedisCacheManager(empRedisTemplate);
        //key多了一个前缀

        //使用前缀,默认会将CacheName作为key的前缀
        cacheManager.setUsePrefix(true);

        return cacheManager;
    }

注意:每一个需要存储的对象,都需要设置对应cacheManager,其中的一个需要 @Primary 设置为默认的cacheManager。在使用缓存的时候,选择对应的cacheManager

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer ser = new Jackson2JsonRedisSerializer(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }
    @Bean
    public RedisTemplate deptRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer ser = new Jackson2JsonRedisSerializer(Department.class);
        template.setDefaultSerializer(ser);
        return template;
    }



    //CacheManagerCustomizers可以来定制缓存的一些规则
    @Primary  //将某个缓存管理器作为默认的
    @Bean
    public RedisCacheManager employeeCacheManager(RedisTemplate empRedisTemplate){
        RedisCacheManager cacheManager = new RedisCacheManager(empRedisTemplate);
        //key多了一个前缀

        //使用前缀,默认会将CacheName作为key的前缀
        cacheManager.setUsePrefix(true);

        return cacheManager;
    }

    @Bean
    public RedisCacheManager deptCacheManager(RedisTemplate deptRedisTemplate){
        RedisCacheManager cacheManager = new RedisCacheManager(deptRedisTemplate);
        //key多了一个前缀

        //使用前缀,默认会将CacheName作为key的前缀
        cacheManager.setUsePrefix(true);

        return cacheManager;
    }
}

使用时选择对应的cacheManager

   @Cacheable(cacheNames = "dept",cacheManager = "deptCacheManager")
    public Department getDeptById(Integer id){
        System.out.println("查询部门"+id);
        Department department = departmentMapper.getDeptById(id);
        return department;
    }

ps:不使用 @Cacheable,手动缓存

    @Qualifier("deptCacheManager")
    @Autowired
    RedisCacheManager deptCacheManager;

    // 使用缓存管理器得到缓存,进行api调用
    public Department getDeptById(Integer id){
        System.out.println("查询部门"+id);
        Department department = departmentMapper.getDeptById(id);

        //获取某个缓存
        Cache dept = deptCacheManager.getCache("dept");
        dept.put(id,department);

        return department;
    }

你可能感兴趣的:(Spring Boot缓存)