Spring series cache annotation @Cacheable @CacheEvit @CachePut usage posture introduction

SpringBoot series cache annotation @Cacheable @CacheEvit @CachePut usage posture introduction

In version 3.1, Spring provides an annotation-based caching strategy, which is actually very smooth to use. This article will briefly introduce and explain several commonly used annotations. Those who need it can try it out.

The main knowledge points of this article:

  • @Cacheable: If the cache exists, use the cache; if it does not exist, execute the method and insert the result into the cache
  • @CacheEvit: invalidate cache
  • @CachePut: update the cache

<!-- more -->

I. Project Environment

1. Project dependencies

This project is developed with the help of SpringBoot 2.2.1.RELEASE+ maven 3.5.3+ IDEA+redis5.0

Open a web service for testing

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

Use the default configuration throughout, redis native, port 6379, no password

II. Introduction to Cache Annotations

1.@Cacheable

This annotation is used to decorate a method or class. When we access the method it decorates, it is preferentially obtained from the cache. If the cache exists, the cached value is directly obtained; when the cache does not exist, the method is executed and the result is written to the cache.

This annotation, there are two more core settings

	/**
	 * 与 cacheNames 效果等价
	 */
	@AliasFor("cacheNames")
	String[] value() default {};

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

	/**
	 * 缓存key
	 */
	String key() default "";

cacheNames can be understood as the prefix of the cache key, which can be the key variable cached by the component; when the key is not set, use method parameters to initialize, note that the key is a SpEL expression, so if you want to write a string, enclose it in single quotes

A simple gesture of use

/**
 * 首先从缓存中查,查到之后,直接返回缓存数据;否则执行方法,并将结果缓存
 * <p>
 * redisKey: cacheNames + key 组合而成 --> 支持SpEL
 * redisValue: 返回结果
 *
 * @param name
 * @return
 */
@Cacheable(cacheNames = "say", key = "'p_'+ #name")
public String sayHello(String name) {
    return "hello+" + name + "-->" + UUID.randomUUID().toString();
}

If we pass the parameter as yihuihui, then the cache key issay::p_yihuihui

In addition to the above three configuration values, @Cacheablethe children's shoes who view the annotation source code can see that there are conditionsettings, which means that the cache will only be written when the conditions set by it are met.

/**
 * 满足condition条件的才写入缓存
 *
 * @param age
 * @return
 */
@Cacheable(cacheNames = "condition", key = "#age", condition = "#age % 2 == 0")
public String setByCondition(int age) {
    return "condition:" + age + "-->" + UUID.randomUUID().toString();
}

In the above case, when the age is an even number, the cache is used; otherwise, the cache is not written.

Next is the unlessparameter. As you can see from the name, it means that the cache is written only when the condition is not met.

/**
 * unless, 不满足条件才写入缓存
 *
 * @param age
 * @return
 */
@Cacheable(cacheNames = "unless", key = "#age", unless = "#age % 2 == 0")
public String setUnless(int age) {
    return "unless:" + age + "-->" + UUID.randomUUID().toString();
}

2. @CachePut

Write the return result of the method to the cache regardless of whether there is a cache; suitable for cache updates

/**
 * 不管缓存有没有,都写入缓存
 *
 * @param age
 * @return
 */
@CachePut(cacheNames = "t4", key = "#age")
public String cachePut(int age) {
    return "t4:" + age + "-->" + UUID.randomUUID().toString();
}

3. @CacheEvict

This is what we understand to delete the cache

/**
 * 失效缓存
 *
 * @param name
 * @return
 */
@CacheEvict(cacheNames = "say", key = "'p_'+ #name")
public String evict(String name) {
    return "evict+" + name + "-->" + UUID.randomUUID().toString();
}

4. @Caching

In actual work, we often encounter a scenario where data changes and multiple caches are updated. For this scenario, we can @Cachingachieve this by

/**
 * caching实现组合,添加缓存,并失效其他的缓存
 *
 * @param age
 * @return
 */
@Caching(cacheable = @Cacheable(cacheNames = "caching", key = "#age"), evict = @CacheEvict(cacheNames = "t4", key = "#age"))
public String caching(int age) {
    return "caching: " + age + "-->" + UUID.randomUUID().toString();
}

The above is the combined operation

  • Get data from the caching::agecache , execute the method and write it to the cache when it does not exist;
  • Invalidate cachet4::age

5. What happens to the cache when there is an exception?

The above cases are all normal scenarios. When the method throws an exception, what happens to the cache performance?

/**
 * 用于测试异常时,是否会写入缓存
 *
 * @param age
 * @return
 */
@Cacheable(cacheNames = "exception", key = "#age")
@Cacheable(cacheNames = "say", key = "'p_yihuihui'")
public int exception(int age) {
    return 10 / age;
}

According to the actual measurement results, age==0at that time , the above two caches would not succeed.

6. Test Cases

Next, verify that the cache annotations are consistent with those described above

@RestController
public class IndexRest {
    @Autowired
    private BasicDemo helloService;

    @GetMapping(path = {"", "/"})
    public String hello(String name) {
        return helloService.sayHello(name);
    }
}

The above is mainly a verification @Cacheableannotation. If the cache misses, the returned result should be different each time. However, when you actually access it, you will find that the returned result is the same.

curl http://localhost:8080/?name=yihuihui

Invalidate cache

@GetMapping(path = "evict")
public String evict(String name) {
    return helloService.evict(String.valueOf(name));
}

Invalidate the cache, which needs to be used in conjunction with the above case

curl http://localhost:8080/evict?name=yihuihui
curl http://localhost:8080/?name=yihuihui

The rest of the other related test classes are easier to understand, and the corresponding code is posted together

@GetMapping(path = "condition")
public String t1(int age) {
    return helloService.setByCondition(age);
}

@GetMapping(path = "unless")
public String t2(int age) {
    return helloService.setUnless(age);
}

@GetMapping(path = "exception")
public String exception(int age) {
    try {
        return String.valueOf(helloService.exception(age));
    } catch (Exception e) {
        return e.getMessage();
    }
}

@GetMapping(path = "cachePut")
public String cachePut(int age) {
    return helloService.cachePut(age);
}

7. Summary

Finally, the management summarizes several cache annotations provided by Spring

  • @Cacheable: If the cache exists, take it from the cache; otherwise, execute the method and write the return result to the cache
  • @CacheEvit: invalidate cache
  • @CachePut: refresh cache
  • @Caching: both annotation combinations

Although the above can meet common cache usage scenarios, there is a very important point that is not explained. How should the cache invalidation time be set? ? ?

How to set a different cache invalidation time for each cache, see you in the next blog post, I am a gray, welcome to pay attention to the public account of Changcao一灰灰blog

III. Source code and related knowledge points that cannot be missed

0. Project

1. A gray blog

It is not as good as a letter. The above content is purely from the family. Due to limited personal ability, there are inevitably omissions and mistakes. If you find bugs or have better suggestions, you are welcome to criticize and correct them. Thank you very much.

The following is a gray personal blog, recording all blog posts in study and work, welcome everyone to visit

a grey blog

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324145811&siteId=291194637