springboot与缓存—使用、原理、整合redis

      Java缓存接口标准JSR-107:Java Caching定义了5个核心接口,分别是CachingProvider(缓存提供者), CacheManager(缓存管理器), Cache(缓存组件), Entry (key-value对)和Expiry(条目有效期),他们的关系如下图所示。但是该缓存规范,整合难度较大。Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;

           文档:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-caching

springboot与缓存—使用、原理、整合redis_第1张图片

1、接口、缓存注解、key/value生成策略

Cache 缓存接口,定义缓存操作。实现:RedisCache、EhCache等(根据缓存技术不同来实现Cache接口)
CacheManager 缓存接口,缓存管理器,管理各种缓存(Cache)组件
@Cacheable 可缓存的,标注方法,能够根据方法的请求参数对其结果进行缓存
@CacheEvict 清空缓存
@CachePut 更新缓存
@EnableCaching 开启基于注解的缓存
keyGenerator 缓存数据时key生成策略
serialize 缓存数据时value序列化策略

2、使用cache缓存步骤(不使用缓存时,每次访问都会连接数据库)

      a、引入依赖


	org.springframework.boot
	spring-boot-starter-cache

      b、开启基于注解的缓存 @EnableCaching

springboot与缓存—使用、原理、整合redis_第2张图片

     c、标注缓存注解@Cacheable:标注方法可缓存;@CacheEvict:缓存清除;@CachePut:缓存更新等;支持spel表达式,如下图;参考官方文档:https://docs.spring.io/spring/docs/5.1.0.BUILD-SNAPSHOT/spring-framework-reference/integration.html#cache

springboot与缓存—使用、原理、整合redis_第3张图片

package com.example.cache.service;

import com.example.cache.bean.Employee;
import com.example.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

//@Service:将service加载到容器中
//CacheConfig:公共的缓存配置;cacheNames = "emp":之后的value="emp"就可以不用写
@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {
    @Autowired
    EmployeeMapper employeeMapper;

    /**
     *  @Cacheable:将方法的运行结果进行保存,以后再要相同的数据,直接从缓存中获取,不用调用方法
     * CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每个缓存组件有自己唯一的名字
     * 属性:(缓存数据时使用key-value的形式)
     *      cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存
     *      key:缓存数据使用的key;可以用她来指定。默认是使用方法参数的值 键值对:1-方法的返回值
     *          参数id的值:#id相当于#root.args[0]
     *      keyGenerator:key的生成器;可以自己指定key的生成器组件id
     *          ——key和keyGenerator不可同时使用
     *      cacheManager:指定缓存管理器(从哪个缓存管理器中取);cacheResolver:指定缓存解析器
     *      condition:指定符合条件的情况下才缓存;condition="#id>0":id大于0的时候才缓存
     *      unless:否定缓存;当unless指定的true,方法的返回值就不会被缓存;可以获取到结果进行判断
     *          unless="#result == null":结果为空时不缓存
     *      sync:缓存是否使用异步模式
     * @param id
     * @return
     */
    @Cacheable(cacheNames = {"emp"})
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
    /**
     * @CachePut:既调用方法,又更新缓存数据;
     * 修改了数据库的某个数据,同时更新缓存
     * 运行过程:
     *  1、先调用目标方法
     *  2、将目标方法的结果缓存起来
     * 更新后重新查询出的数据是更新前的数据:
     *  ——(key默认使用方法参数的值)查询是的缓存是@Cacheable缓存的值,key是1;@CachePut更新后缓存的值:key是传入的employee对象
     *  ——所以要统一key:key="#employee.id";key="#result.id";
     */
    @CachePut(value = "emp",key="#employee.id")
    public Employee updateEmp(Employee employee){
        employeeMapper.updateEmp(employee);
        return employee;
    }
    /**
     *@CacheEvict缓存清除
     * key:指定要清楚的数据
     * allEntries=true:指定清除这个缓存中的所有数据
     * beforeInvocation = false;缓存的清除是否在方法之前执行;false:在方法执行之后清除
     */
    @CacheEvict(value="emp",key="#id")
    public void deleteEmp(Integer id){
    }

    /**
     * 指定多个缓存规则
     */
    @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);
    }
}

3、整合redis进行缓存;开发中常使用缓存中间件:redis、memcahed、ehcache;

     引入redis启动器


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

    配置redis信息

spring.redis.host=127.0.0.1
spring.redis.password=

 

springboot与缓存—使用、原理、整合redis_第4张图片

     使用redis

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootCacheApplicationTests implements Serializable {
    @Autowired
	EmployeeMapper employeeMapper;
    @Autowired
	StringRedisTemplate stringRedisTemplate;//key-value都是操作字符串
	@Autowired
	RedisTemplate redisTemplate;//key-value都是对象
    //@Autowired
	//RedisTemplate empRedisTemplate;
    
    /**
	 * Redis常见的五大数据类型
	 * 	String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
	 * 	stringRedisTemplate.opsForValue()[操作String(字符串)]
	 * 	stringRedisTemplate.opsForList()[操作List(列表)]
	 * 	stringRedisTemplate.opsForSet()[操作Set(集合)]
	 * 	stringRedisTemplate.opsForHash()[操作Hash(散列)]
	 * 	stringRedisTemplate.opsForZSet()[操作ZSet(有序集合)]
	 */
	@Test
	public  void test01(){
		//stringRedisTemplate.opsForValue().append("msg","hello");
		String msg = stringRedisTemplate.opsForValue().get("msg");
		System.out.println(msg);
	}

    //测试保存对象(需要序列化)
	@Test
	public  void test02(){
		Employee empById = employeeMapper.getEmpById(1);
		//默认如果保存对象,使用jdk序列化机制,序列化后的数据保存到redis
		redisTemplate.opsForValue().set("emp-01",empById);
		//将数据以json的方式保存:1、自己将对象转化为json;2、redisTemplate默认的序列化规则:改变默认的序列化规则
		//empRedisTemplate.opsForValue().set("emp-01",empById);
	}
}

        自定义序列化器,改变默认的序列化规则;使其保存的数据为json格式,而不是序列化格式。

package com.example.cache.config;

import com.example.cache.bean.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.List;

@Configuration
public class MyRedisConfig {
    //RedisTemplate:序列化Employee
    @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;
    }
}

     原理:默认使用缓存管理器CacheManager来创建Cache缓存组件,来给缓存中存取数据。加入redis后,开启了RedisCacheConfiguration。RedisCacheManager帮我们创建RedisCache来作为缓存组件。

springboot与缓存—使用、原理、整合redis_第5张图片

4、缓存运行流程  

     @Cacheable:

    a、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;(CacheManager先获取相应的缓存),如果没有Cache组件,第一次获取缓存会自动创建

     b、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;

          key是按照某种策略生成的;默认是使用keyGenerator生成的;默认使用SimpleKeyGenerator生成key

          SimpleKeyGenerator生成key的默认策略:

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

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

      d、将目标方法返回的结果放到缓存中

     @Cacheable标注的方法执行之前来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,如果没有就运行方法,并将结果放入缓存。以后再来调用,就可以使用缓存中的数据;

5、缓存原理

  a、自动配置类CacheAutoConfiguration给容器中导入组件

springboot与缓存—使用、原理、整合redis_第6张图片

   b、各缓存的配置类

springboot与缓存—使用、原理、整合redis_第7张图片

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

springboot与缓存—使用、原理、整合redis_第8张图片

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

springboot与缓存—使用、原理、整合redis_第9张图片

你可能感兴趣的:(redis,SpringBoot)