1、在pom.xml中引入相关依赖

自己导入web,mybatis,mysql的依赖

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

2、配置redisconfig,在实际项目中可以通过配置KeyGenerator来指定缓存信息的key的生成规则

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {
        RedisTemplate<Object,Employee> template = new RedisTemplate<Object, Employee>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }
    // @Primary将某个缓存管理器作为默认的
    @Bean
    public RedisCacheManager employeeRedisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofDays(1))   // 设置缓存过期时间为一天
                        .disableCachingNullValues()     // 禁用缓存空值,不缓存null校验
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new
                                GenericJackson2JsonRedisSerializer()));     // 设置CacheManager的值序列化方式为json序列化,可加入@Class属性
        return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();     // 设置默认的cache组件
    }


    //可以CacheManagerCuetomizers可以来定制缓存一些规则
    /*@Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //设置CacheManager的值序列化方式为json序列化
        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(jsonSerializer);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(pair);
        //设置默认超过期时间是30秒
        defaultCacheConfig.entryTtl(Duration.ofSeconds(300));
        //初始化RedisCacheManager
        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }*/

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
 
 
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        @SuppressWarnings("rawtypes")
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
 
 
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
 
 
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
 
 
        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

	
    
    //缓存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory lettuceConnectionFactory) {
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        // 设置缓存管理器管理的缓存的默认过期时间
        defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofMinutes(60))
                // 不缓存空值
                .disableCachingNullValues();

        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("my-redis-cache1");

        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("my-redis-cache1", defaultCacheConfig.entryTtl(Duration.ofMinutes(50)));

        RedisCacheManager cacheManager = RedisCacheManager.builder(lettuceConnectionFactory)
                .cacheDefaults(defaultCacheConfig)
                .initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }



}

3、配置文件 application

spring.datasource.url=jdbc:mysql://ip:3306/springboot_cache?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 开启驼峰命名匹配
mybatis.configuration.map-underscore-to-camel-case=true

logging.level.com.atguigu.chche.mapper=debug

#debug=true

spring.redis.host=ip

4 配置启动类-启动缓存

@SpringBootApplication
@MapperScan("com.atguigu.cache.mapper")
@EnableCaching
public class Boot01CacheApplication {

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

}

5 缓存注解@Cacheable、@CacheEvict、@CachePut详解

5.1 注解使用的地方

用在方法上表示:该方法的返回值将被缓存起来。
用在类上表示:表示该类的所有方法都支持该注解。

5.2 key的生成策略


key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

  @Cacheable(value="users", key="#id")

   public User find(Integer id) {

      returnnull;

   }
   @Cacheable(value="users", key="#p0")

   public User find(Integer id) {

      returnnull;

   }
   @Cacheable(value="users", key="#user.id")

   public User find(User user) {

      returnnull;

   }
   @Cacheable(value="users", key="#p0.id")

   public User find(User user) {

      returnnull;

   }

5.3 root对象的使用

除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。

5.4 @CachePut
在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。 一般使用在保存,更新方法中。

5.5 @CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。

allEntries属性
allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。

beforeInvocation属性
清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。该属性表示的是是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。


6. 缓存注解的使用

package com.atguigu.cache.service;

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

/**
 * Created with IntelliJ IDEA.
 * Author: Coffee君
 * Time: 2020/3/17 11:11
 * Description: No Description
 */
@CacheConfig(cacheNames="emp") // 抽取缓存的公共配置
@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /*
    * 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不再调用方法;
    *
    * CacheManager管理多个Cache组件的,对缓存的真正crud操作再Chche组件中,每一个缓存组件中有自己唯一一个名字;
    * 几个属性:
    *   cacheNames/value:制定缓存的名字;将方法的返回解惑放在哪个缓存中,是数组的方式,可以指定多个缓存;
    *
    *
    *   key:缓存数据使用的key;可以用它指定。默认是使用方法参数的值 1-方法的返回值
    *           编写spel; #id;参数id的值 #a0 #p0     key= "#root.methodName+'['+#id+'}'"
    *   keyGenerator: key的生成器;可以自己制定key的生成器的组件id
    *       key/keyGenertor 二选一使用
    *   cacheManger: 制定缓存管理器; 或者指定缓存解析器
    *   condition: 指定符合条件的情况下才缓存
    *       condition = "#a0>1":第一个参数的值》1的时候才缓存
    *
    *   unless:否定缓存;当unless制定的条件为true,方法的返回值就不会缓存,可以对获取的结果进行判断
    *       unless = “result == null”
    *   sysc:是否使用异步模式
    *
    *原理:
    *   1、自动配置类;CacheAuto
    *
    * */
    @Cacheable(/*value =  {"emp"}*/ /*,key= "#root.methodName+'['+#id+'}'" ,condition = "#a0>1",unless = "#a0==2"*/)
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

    /*
    * @CachePut: 既调用方法,有更新缓存数据;同步更新缓存
    * 修改了数据库的某个数据,同时更新缓存;
    * 运行时机:
    * 1、先调用目标方法
    * 2、将目标方法的结果缓存起来
    *
    * 测试步骤:
    *   1、查询1号员工;查到的结果会放在缓存中
    *   2、以后查询还是之前的结果
    *   3、更新1号员工;
    *   4、查询1号员工
    *       应该是更新后的员工;
    *           key="employee.id":使用传入的参数的员工id
    *           key="result.id":使用返回后的id
    *               @Cacheable的key是不能用#result
    *       为甚麽是没更新前【1号员工没有在缓存中更新】
    *
    * */
    @CachePut(/*value = "emp" ,*/ key = "#employee.id")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

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

    }

}
package com.shenju;

import java.util.ArrayList;
import java.util.List;

import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class CacheServiceImpl {

	/**
	 * 1 这里面的targetClass=#root.targetClass methodName=#root.methodName
	 * 2 存储的key为 user::class com.shenju.CacheServiceImpl:findUserId_wuk
	 * 
	 * @return
	 */
	@Cacheable(value = "user", key = "targetClass + ':' + methodName + '_' + #p0.name")
	public int findUserId(User user) {

		System.out.println("执行find方法.....");
		return 1;
	}

	/**
	 * 1 #p0 表示第一个参数 
	 * 2 存储的key为 user::class com.shenju.CacheServiceImpl:listUser_2 
	 * 3 unless 缓存条件:判断unless,如果返回false,则放入缓存 
	 * 4 #result 表示的是返回结果
	 */
	@Cacheable(value = "user", key = "targetClass + ':' + methodName + '_' + #p0", unless = "#result.size() <= 0")
	public List<User> listUser(int pageNum, int pageSize) {
		
		System.out.println("执行listUser方法。。。。");
		List<User> users = new ArrayList<>();
		users.add(new User("zhengsan", 22));
		users.add(new User("lisi", 20));

		return users;
	}

	/**
	 * 1 存储的key为: user::class com.shenju.CacheServiceImpl:findUser_wuk
	 * 2 condition 表示的是缓存的条件,只有当为true
	 * 
	 */
	@Cacheable(value = "user", key = "targetClass + ':' + methodName + '_' + #p0.name", condition="#user.age==25")
	public User  findUser(User user) {
		
		System.out.println("执行findUser方法。。。。");
		user.setName("wukaikai");
		return user;
	}

	
	
	@CachePut(value = "user", key = "targetClass + ':' + methodName + '_' + #p0.name")
	public User  save(User user) {
		
		System.out.println("执行save方法。。。。");		
		return user;
	}
}