SpringBoot的默认缓存使用

1,347 阅读5分钟

Spring自带缓存

在不使用Redis等缓存组件时,Spring默认的缓存机制,是通过一个ConcurrentMapCacheManager,来获取ConcurrentMapCache类型的缓存组件,缓存数据都存放在ConcurrentHashMap中

主要注解:

  1. @EnableCaching:标注在主启动类上,表示开启缓存
  2. @Cacheable:标注在方法上,被标注的方法会使用缓存
  3. @CacheEvict
  4. @CachePut

@EnableCaching

@SpringBootApplication
@EnableCaching				// 直接标注在主启动类上即可
public class StudySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(StudySpringBootApplication.class, args);
    }
}

@Cacheable

标注了@Cacheable注解后,当执行到标记的方法时,会先去查询Cache,按照cacheNames/value指定的名字获取到缓存(通过CacheManager,第一次获取缓存时,如果没有Cache组件,会自动创建一个对应的缓存组件),然后到缓存中查询内容,通过一个key来查询(key默认是方法的入参);如果从缓存中找到了对应的值,就直接返回缓存中存放的值,而不调用方法,否则就调用方法,并将该方法的返回值放入缓存中。

@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

	// @Cacheable
    @Cacheable(value = "getEmp")
    public Employee getEmployeeById(Integer id) {
        return employeeMapper.getEmpById(id);
    }

    public void updateEmployee(Employee employee) {
        employeeMapper.updateEmployee(employee);
    }
}

Cacheable各个属性解析

Cacheable源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};			// 同cacheNames,表示当前cache组件的名字,数组形式,可以存放多个name

    @AliasFor("value")
    String[] cacheNames() default {};		// 同value

    String key() default "";				// 缓存数据时使用的key,默认是入参,可以修改,通过SpEL表达式来指定

    String keyGenerator() default "";		// key生成器,配置它可以指定key的生成格式(与key两者选一个用)

    String cacheManager() default "";		// 指定使用哪个缓存管理器

    String cacheResolver() default "";		// 指定使用哪个缓存解析器
		
    String condition() default "";			// 表示缓存在什么条件下生效,通过SpEL表达式配置

    String unless() default "";				// 表示缓存在什么情况下不生效,通过SpEL表达式配置

    boolean sync() default false;			// 表示是否使用异步模式
}

具体使用各个属性

①key

// 这里root.methodName表示当前方法名,#id表示传参中的属性名为id的值
@Cacheable(value = {"getEmp"}, key = "#root.methodName+'['+#id+']'")
public Employee getEmployeeById(Integer id) {
    return employeeMapper.getEmpById(id);
}

通过打断点可以看到,在ConcurrentMapCache中,lookup方法中的key已经称为了设置的结果:方法名+[id]

②keyGenerator

// keyGenerator中的值是注册入容器的KeyGenerator实例的id
@Cacheable(value = {"getEmp"},keyGenerator = "keyGenerator")
public Employee getEmployeeById(Integer id) {
    return employeeMapper.getEmpById(id);
}

注册的KeyGenerator:

@Configuration
public class CacheConfiguration {
    @Bean("keyGenerator")
    public KeyGenerator getKeyGenerator() {
        return new KeyGenerator() {
            /**
            *	method代表调用的方法,objects是入参数组
            */
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                return method.getName() + "--" + Arrays.asList(objects);
            }
        };
  	 }
}

③condition与unless

condition:在表达式满足时,触发缓存机制

unless:当表达式满足时,就不会触发缓存机制

@Cacheable(value = {"getEmp"},keyGenerator = "keyGenerator",condition = "#id>1",unless = "#id==3")
public Employee getEmployeeById(Integer id) {
    return employeeMapper.getEmpById(id);
}

使用上面的代码后,可以发现当浏览器中发送的id<=1或id ==3时,不会触发缓存,每一次请求都会查询数据库;而满足id>1且id!=3时,就会触发缓存,不会重复查询数据库。

@CachePut

@CachePut :既调用方法,又更新了缓存

​ 通常用于修改了数据库的数据后,更新缓存中的数据

执行顺序

​ 1、调用方法

​ 2、将方法的返回值放入缓存中

​ 想要更新数据库时,让标注了@Cacheable的查询方法的缓存也更新,需要:

​ 1、让@CachePut和 @Cacheable的value值相同,

​ 2、key也要相同:这里可以用#employee.id 或 #result.id

​ (result就代表方法的返回值,但是在@Cacheable中不能使用result,

​ 因为Cacheable要先查询缓存,而此时还没有返回值,这样就无法查询)

@CachePut(value = "emp",key = "#result.id")
public Employee updateEmployee(Employee employee) {
    employeeMapper.updateEmployee(employee);
    return employee;
}

在value和key值和前面的标注了@Cacheable注解的查询方法一致的时候,可以发现每一次更新数据的时候,查询中的缓存也被更新了,查询方法不需要执行就可以根据更新后的缓存得到正确的数据。

@CacheEvict

@CacheEvict 每一次调用时删除value缓存中对应key的缓存内容

注解中的特殊属性:

1、allEntries:

​ 默认false:表示每次触发清理时清理key对应的缓存数据;

​ 设置为true:表示每一次清理缓存都会清理全部的缓存,也就删除了其他key的缓存数据

2、beforeInvocation:

​ 默认false:表示在方法体执行完后,才会进行缓存清理的操作

​ 设置为true:表示在方法执行前执行缓存清理操作

作用:设置为true时,如果方法内部出现了异常,也会完成缓存清理的操作,否则不会进行

@CacheEvict(value = "emp",key = "#id",allEntries = true,beforeInvocation = true)
public void deleteEmployee(Integer id) {
    employeeMapper.deleteEmployee(id);
}

@Caching

@Caching注解标注在方法上,可以用来进行相对复杂的缓存操作,如下面,将同一个数据缓存入多种格式的key中

@Caching(   // 可以使用@Caching来进行复杂的缓存操作,如下
        cacheable = {   // 指定一个或多个Cacheable规则
                @Cacheable(value = "emp", key = "'lastName:'+#lastName")
        },
        put = {         // 指定一个或多个CachePut规则
                @CachePut(value = "emp", key = "#result.id"),
                @CachePut(value = "emp", key = "'email:'+#result.email")
        }
)
public Employee getEmployeeByLastName(String lastName) {
    return employeeMapper.getEmployeeByLastName(lastName);
}

@Caching源码

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

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
}

可以看到除了指定cacheable和put之外,还可以指定evict来进行缓存清理的操作。

@CacheConfig

@CacheConfig标注在类上,用来抽取该类中缓存的公共配置(如标注了cacheNames后,下面的方法上就会默认采用这个值作为cacheName【同value】,可以减少冗余代码)

@Service
@CacheConfig(cacheNames = "emp")
public class EmployeeService {
	// ... ...
}

@CacheConfig源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
    String[] cacheNames() default {};

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";
}

可以看到可以设置以上的公共信息