编程知识 cdmana.com

Cache usage of spring series (@ enablecaching, @ cacheable, @ cacheput, @ cacheevict, @ caching, @ cachecon)

This article mainly explains spring Use of cache in .

background

Everyone knows about cache , It is mainly used to improve the query speed of the system .

For example, commodity details in e-commerce , This information usually does not change frequently, but is accessed frequently , We can take this information from db Take it out and put it in the cache ( such as redis in 、 In local memory ), When getting , Get it from the cache first , When not in the cache , Again from db In order to get , Then throw it back into the cache , When the product information is changed , You can eliminate the information in the cache or throw the latest data into the cache .

Spring Provides a complete set of caching solutions , It's especially easy to use , Cache is mainly used by annotation , Commonly used 5 A note , Let's introduce... One by one .

This article will use a lot of spel expression , Take a look at this unfamiliar suggestion first :Spring in Spel Detailed explanation

@EnableCaching: Enable caching

Enable caching , This annotation needs to be added to the configuration class , With this annotation ,spring Just know that you need to use the function of cache , Other cache related annotations will be valid ,spring Mainly through aop Realized , adopt aop To intercept methods that need to use caching , Realize the function of cache .

@Cacheable: Give cache function

effect

@Cacheable Can be marked on a method , It can also be marked on a class . When the tag is on a method, it means that the method supports caching , When the tag is on a class, it means that all methods of the class support caching . For a method that supports caching ,Spring It will cache its return value after it is called , To ensure that the next time the method is executed with the same parameters, the result can be obtained directly from the cache , Instead of executing the method again .Spring When caching the return value of a method, the key value pairs are cached , Value is the return result of the method , As for the key ,Spring Two strategies are supported , Default policy and custom policy , This will be explained later . It should be noted that when a method supporting caching is called inside an object, the caching function will not be triggered [email protected] You can specify three properties ,value、key and condition.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
 String[] value() default {};
 String[] cacheNames() default {};
 String key() default "";
 String condition() default "";
 String unless() default "";
}
 Copy code 

value attribute : Appoint Cache name

value and cacheNames Attributes work the same , You must specify one of , Indicates where the return value of the current method will be cached Cache Upper , Corresponding Cache The name of . It can be a Cache It can be multiple Cache, When you need to specify more than one Cache It's an array .

Can be Cache Imagine as a HashMap, There can be many in the system Cache, Every Cache There is a name , You need to put the return value of the method in which cache , You need to specify... By the name of the cache .

Case study 1

below list Method adds the function of caching , Put the results in the cache cache1 in .

package com.javacode2018.cache.demo1;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Component
public class ArticleService {

    @Cacheable(cacheNames = {"cache1"})
    public List<String> list() {
        System.out.println(" Get article list !");
        return Arrays.asList("spring", "mysql", "java High concurrency ", "maven");
    }
}
 Copy code 

Here's a configuration class MainConfig1, Have to add @EnableCaching Annotations are used to enable caching .

Then you need to define a in the configuration class bean: Cache manager , The type is CacheManager,CacheManager This is an interface , There are several implementations ( For example, use redis、ConcurrentMap To store cached information ), Here we use ConcurrentMapCacheManager, For internal use ConcurrentHashMap Store cached information directly locally jvm In the memory , However, the online environment is generally clustered , Can pass redis Realization , The next article introduces spring Cache Integration redis.

package com.javacode2018.cache.demo1;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@EnableCaching //@0
@ComponentScan
@Configuration
public class MainConfig1 {

    //@1: Cache manager 
    @Bean
    public CacheManager cacheManager() {
        // Create a cache manager (ConcurrentMapCacheManager: Internal use ConcurrentMap Realized ), The constructor is used to specify the name of the cache , You can specify multiple 
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager("cache1");
        return cacheManager;
    }

}
 Copy code 

Let's have a test class ,2 Secondary call list Methods to see the effect

package com.javacode2018.cache;

import com.javacode2018.cache.demo1.ArticleService;
import com.javacode2018.cache.demo1.MainConfig1;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CacheTest {

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MainConfig1.class);
        context.refresh();
        ArticleService articleService = context.getBean(ArticleService.class);
        System.out.println(articleService.list());
        System.out.println(articleService.list());
    }

}
 Copy code 

Output

 Get article list !
[spring, mysql, java High concurrency , maven]
[spring, mysql, java High concurrency , maven]
 Copy code 

You can see from the first line that , Enter for the first time list Inside the method , The second time did not enter list Methods the internal , But from the cache .

key attribute : Customize key

key Property to specify Spring The result returned by the cache method corresponds to key Of , It says you can Cache Understood as a hashMap, Cache with key->value The form is stored in hashmap in ,value You need to cache values ( That is, the return value of the method )

key Property support SpEL expression ; When we don't specify this property ,Spring Will generate... Using the default policy key(org.springframework.cache.interceptor.SimpleKeyGenerator), By default, method parameters are created key.

Custom policy means that we can use SpEL Expression to specify our key, there SpEL Expressions can use method parameters and their corresponding properties , When using method parameters, we can directly use “# Parameter name ” perhaps “#p Parameters index”.

Spring It also provides us with a root Objects can be used to generate key, Through the root Object we can get the following information .

The attribute name describe Example
methodName Current method name #root.methodName
method The current method #root.method.name
target Currently called object #root.target
targetClass Of the currently called object class#root.targetClass
args Array of current method parameters #root.args[0]
caches The currently called method uses Cache#root.caches[0].name

Here we mainly look at the custom policy .

Case study 2

ArticleService Add the following code

@Cacheable(cacheNames = {"cache1"}, key = "#root.target.class.name+'-'+#page+'-'+#pageSize")
public String getPage(int page, int pageSize) {
    String msg = String.format("page-%s-pageSize-%s", page, pageSize);
    System.out.println(" from db Get data in :" + msg);
    return msg;
}
 Copy code 

com.javacode2018.cache.CacheTest New test cases

@Test
public void test2() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig1.class);
    context.refresh();
    ArticleService articleService = context.getBean(ArticleService.class);
    
    //page=1,pageSize=10 call 2 Time 
    System.out.println(articleService.getPage(1, 10));
    System.out.println(articleService.getPage(1, 10));
    
    //page=2,pageSize=10 call 2 Time 
    System.out.println(articleService.getPage(2, 10));
    System.out.println(articleService.getPage(2, 10));

    {
        System.out.println(" Print it out below cache1 In the cache key list ");
        ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
        ConcurrentMapCache cache1 = (ConcurrentMapCache) cacheManager.getCache("cache1");
        cache1.getNativeCache().keySet().stream().forEach(System.out::println);
    }
}
 Copy code 

Operation output

 from db Get data in :page-1-pageSize-10
page-1-pageSize-10
page-1-pageSize-10
 from db Get data in :page-2-pageSize-10
page-2-pageSize-10
page-2-pageSize-10
 Print it out below cache1 In the cache key list 
com.javacode2018.cache.demo1.ArticleService-getPage-1-10
com.javacode2018.cache.demo1.ArticleService-getPage-2-10
 Copy code 

condition attribute : Control the usage conditions of the cache

occasionally , Maybe we want to query without going to the cache , At the same time, the returned results should not be cached , Then you can pass condition Property to implement ,condition Property is empty by default , Indicates that all calls will be cached , Its value is through spel Expression to specify , When it comes to true When, it means to try to get... From the cache first ; If it doesn't exist in the cache , Then just the method , And throw the return value of the method into the cache ; When it comes to false When , Don't walk the cache 、 Direct execution method 、 And the returned results will not be thrown into the cache .

Its value spel And key Properties are similar to .

Case study 3

ArticleService Add the following code , The second argument to the method cache Used to control whether to cache , take condition The value of is specified as #cache

/**
 *  Through the article id Access to the article 
 *
 * @param id     article id
 * @param cache  Whether to try to get... From the cache 
 * @return
 */
@Cacheable(cacheNames = "cache1", key = "'getById'+#id", condition = "#cache")
public String getById(Long id, boolean cache) {
    System.out.println(" get data !");
    return "spring cache :" + UUID.randomUUID().toString();
}
 Copy code 

Let's have a test case ,4 Secondary call getById Method , front 2 And the last time cache Parameters are true, The first 3 Next is false

@Test
public void test3() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig1.class);
    context.refresh();
    ArticleService articleService = context.getBean(ArticleService.class);

    System.out.println(articleService.getById(1L, true));
    System.out.println(articleService.getById(1L, true));
    System.out.println(articleService.getById(1L, false));
    System.out.println(articleService.getById(1L, true));
}
 Copy code 

Operation output

 get data !
spring cache :27e7c11a-26ed-4c8b-8444-78257daafed5
spring cache :27e7c11a-26ed-4c8b-8444-78257daafed5
 get data !
spring cache :05ff7612-29cb-4863-b8bf-d1b7c2c192b7
spring cache :27e7c11a-26ed-4c8b-8444-78257daafed5
 Copy code 

As you can see from the output , The first 1 Time and number 3 Time , It's all in the method , and 2 and 4 The cache is gone , After the first execution, the result is thrown into the cache , therefore 2 and 4 this 2 The results obtained for the first time and the 1 Time is the same .

unless attribute : Controls whether the results need to be thrown into the cache

Used to override the method cache SpEL expression . And condition Different , This expression is evaluated after the method is called , Therefore, you can quote the result . The default value is “”, This means that the cache will never be rejected .

Premise is condition Is empty or true Under the circumstances ,unless It works ,condition by false When ,unless Invalid ,unless by true, The result returned by the method will not be thrown into the cache ;unless by false, The result returned by the method will be thrown into the cache .

Its value spel And key Properties are similar to .

Case study 4

Here's a case , When the return result is null When , Do not cache results ,ArticleService Add the following code

Map<Long, String> articleMap = new HashMap<>();
/**
 *  Access to the article , Get it from the cache first , If the result obtained is empty , Don't put the results in the cache 
 *
 * @param id
 * @return
 */
@Cacheable(cacheNames = "cache1", key = "'findById'+#id", unless = "#result==null")
public String findById(Long id) {
    this.articleMap.put(1L, "spring series ");
    System.out.println("---- Access to the article :" + id);
    return articleMap.get(id);
}
 Copy code 

Let's have a test case ,4 Secondary call findById, front 2 Secondary data , Back 2 Return times null, And put... In the cache key Printed it out

@Test
public void test4() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig1.class);
    context.refresh();
    ArticleService articleService = context.getBean(ArticleService.class);

    System.out.println(articleService.findById(1L));
    System.out.println(articleService.findById(1L));
    System.out.println(articleService.findById(3L));
    System.out.println(articleService.findById(3L));

    {
        System.out.println(" Print out below cache1 In the cache key list ");
        ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
        ConcurrentMapCache cache1 = (ConcurrentMapCache) cacheManager.getCache("cache1");
        cache1.getNativeCache().keySet().stream().forEach(System.out::println);
    }
}
 Copy code 

Operation output

---- Access to the article :1
spring series 
spring series 
---- Access to the article :3
null
---- Access to the article :3
null
 Print out below cache1 In the cache key list 
findById1
 Copy code 

It can be seen that the article id by 1 The results are cached , file id by 3 Is not cached .

condition and unless contrast

There are some problems in the use of cache 2 A little bit :

  1. Query whether there is data in the cache
  2. If there is no data in the cache , Then execute the target method , Then throw the method results into the cache .

spring Pass through condition and unless For this 2 Point to intervene .

condition Above the scope 2 A process , When it comes to true When , Will try to get data from the cache , without , Will execute method , Then throw the method return value into the cache ; If false, Call the target method directly , And the results will not be put in the cache .

and unless stay condition by true Only in the case of , Used to judge the first 2 Point in , Whether not to throw the results into the cache , If true, Then the result will not be thrown into the cache , If false, Then the result will be thrown into the cache , also unless Can be used in spel The expression passes through #result To get the return value of the method .

@CachePut: Put the results in the cache

effect

@CachePut It can also be marked on classes or methods , The marked method is called every time , Then after the method is executed , Will throw the method results into the cache ; When marked on a class , It is equivalent to marking... On all methods of the class @CachePut.

Yes 3 In this case , The result will not be lost to the cache

  1. When the method is thrown out
  2. condition The result of the calculation is false When
  3. unless The result of the calculation is true When

Source code and Cacheable similar , Contains parameters similar to .

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
 String[] value() default {};
 String[] cacheNames() default {};
 String key() default "";
 String condition() default "";
 String unless() default "";
}
 Copy code 
  • value and cacheNames: Used to specify the cache name , You can specify multiple
  • key: The cache key,spel expression , Reference to writing method @Cacheable Medium key
  • condition:spel expression , Writing and @Cacheable Medium condition equally , When it is empty or the calculation result is true When , The return value of the method will be thrown into the cache ; Otherwise, the result will not be thrown into the cache
  • unless: When condition Is null or the calculation result is true When ,unless Will work ;true: The result will not be thrown into the cache ,false: The result will be thrown into the cache .

Case study 5

Here's a case , Realize the operation of adding new articles , Then throw the article into the cache , Pay attention to the following @CachePut Medium cacheNames、key 2 Parameters and cases 4 in findById On the way @Cacheable The same as in China , Explain that they share a cache ,key It's the same , So when add After method execution , Then call findById Method , You can get data directly from the cache .

@CachePut(cacheNames = "cache1", key = "'findById'+#id")
public String add(Long id, String content) {
    System.out.println(" New article :" + id);
    this.articleMap.put(id, content);
    return content;
}
 Copy code 

The test case

@Test
public void test5() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig1.class);
    context.refresh();
    ArticleService articleService = context.getBean(ArticleService.class);

    // newly added 3 Articles , because add The method has @CachePut annotation , So after adding, it will be automatically thrown into the cache 
    articleService.add(1L, "java High concurrency series ");
    articleService.add(2L, "Maven Master Series ");
    articleService.add(3L, "MySQL Master Series ");

    // And then call findById obtain , See if you can walk 
    System.out.println(" call findById Method , Will try to get... From the cache ");
    System.out.println(articleService.findById(1L));
    System.out.println(articleService.findById(2L));
    System.out.println(articleService.findById(3L));

    {
        System.out.println(" Print it out below cache1 In the cache key list ");
        ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
        ConcurrentMapCache cache1 = (ConcurrentMapCache) cacheManager.getCache("cache1");
        cache1.getNativeCache().keySet().stream().forEach(System.out::println);
    }
}
 Copy code 

Operation output

 New article :1
 New article :2
 New article :3
 call findById Method , Will try to get... From the cache 
java High concurrency series 
Maven Master Series 
MySQL Master Series 
 Print out below cache1 In the cache key list 
findById3
findById2
findById1
 Copy code 

Take a look at the output , And then take a look at findById Method code

@Cacheable(cacheNames = "cache1", key = "'findById'+#id", unless = "#result==null")
public String findById(Long id) {
    this.articleMap.put(1L, "spring series ");
    System.out.println("---- Access to the article :" + id);
    return articleMap.get(id);
}
 Copy code 

There is no... In the output ---- Such content , Description call findById Method gets the result from the cache .

@CacheEvict: Cache cleanup

effect

Used to clear the cache ,@CacheEvict It can also be marked on classes or methods , Marked on the method , When the target method is called , Clears the specified cache ; When marked on a class , It is equivalent to marking... On all methods of the class @CacheEvict.

Take a look at the source code , Please take a detailed look at the comments of each parameter .

public @interface CacheEvict {

    /**
     * cache The name of , and cacheNames The effect is the same 
     */
    String[] value() default {};

    /**
     * cache The name of , and cacheNames The effect is the same 
     */
    String[] cacheNames() default {};

    /**
     *  The cache key, Refer to the above @Cacheable Annotated key
     */
    String key() default "";

    /**
     * @CacheEvict  The conditions for the annotation to take effect , The value is spel expression , Refer to the above  @Cacheable In the annotations condition
     */
    String condition() default "";

    /**
     *  Clean or not  cacheNames  All cache information in the specified cache , The default is false
     *  You can put a cache Imagine as a HashMap, When  allEntries  by true When , amount to HashMap.clear()
     *  When  allEntries  by false When , Will only kill key Corresponding data , amount to HashMap.remove(key)
     */
    boolean allEntries() default false;

    /**
     *  What do you want to do to clear ( Before method execution  or  After the method is executed successfully )
     * true:@CacheEvict  Before the annotated method is executed , Perform the purge operation 
     * false:@CacheEvict  After the marked method is executed successfully , Perform the purge operation , When the method pops an exception , No cleanup will be performed 
     */
    boolean beforeInvocation() default false;
}
 Copy code 

condition attribute

@CacheEvict The conditions for the annotation to take effect , The value is spel expression , Refer to the above @Cacheable In the annotations condition

Which caches will be cleared ?

By default cacheNames In the specified cache key The cache information specified by the parameter .

But when allEntries by true When , Will clear cacheNames All cache information in the specified cache .

When to clear the cache ?

This is through beforeInvocation Parameter controlled , This parameter defaults to false, By default, the purge operation will be performed after the target method is successfully executed , If method throws an exception out , No cleanup will be performed ;

If beforeInvocation   by true, Then the cache cleanup operation will be performed before the method is executed , After the method is executed, it will not be executed again .

Case study 6

ArticleService Add a new method , Use @CacheEvict mark , After this method is executed , Will clean up cache1 in key=findById+ Parameters id Cache information for , Be careful cacheNames and key The sum of the values of the two parameters findById In this 2 The values of the two parameters are the same .

@CacheEvict(cacheNames = "cache1", key = "'findById'+#id") //@1
public void delete(Long id) {
    System.out.println(" Delete articles :" + id);
    this.articleMap.remove(id);
}
 Copy code 

New test cases , The notes are clear , No explanation

@Test
public void test6() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig1.class);
    context.refresh();
    ArticleService articleService = context.getBean(ArticleService.class);

    // The first 1 Secondary call findById, Not in cache , Then call the method , Throw the results into the cache 
    System.out.println(articleService.findById(1L));
    // The first 2 Secondary call findById, There is... In the cache , Get directly from the cache 
    System.out.println(articleService.findById(1L));

    // Delete ,delete The method has @CacheEvict Method , Will clear the cache 
    articleService.delete(1L);

    // Call again findById Method , It is found that there is no... In the cache , The target method... Will be called 
    System.out.println(articleService.findById(1L));
}
 Copy code 

Operation output

---- Access to the article :1
spring series 
spring series 
 Delete articles :1
---- Access to the article :1
spring series 
 Copy code 

Called 3 Time findById, The first 1 Time , Not in cache , So I went inside the method , Then throw the result into the cache , The first 2 There are... In the secondary cache , So get from the cache , And then we did delete Method , After this method is executed , Will clear the articles in the cache id by 1L Article information of , Finally, execute the third findById Method , No data is found in the cache at this time , Then go inside the target method , The internal output of the target method ---- Content .

@Caching: Cache annotation group

When we use on a class or the same method at the same time @Cacheable、@CachePut and @CacheEvic When more than one of these annotations , You can use @Caching This annotation implements .

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {

 Cacheable[] cacheable() default {};

 CachePut[] put() default {};

 CacheEvict[] evict() default {};

}
 Copy code 

@CacheConfig: Extract public configuration

This annotation is marked on the class , Several other cache annotations can be (@Cacheable、@CachePut and @CacheEvic) The public parameters of are extracted and placed in @CacheConfig in .

For example, when there are many methods in a class that need to be used (@Cacheable、@CachePut and @CacheEvic) When these annotations are cached , You can take a look at this 3 An annotated source code , They have many common attributes , such as :cacheNames、keyGenerator、cacheManager、cacheResolver, If these attribute values are the same , It can be extracted , Put it in @CacheConfig in , But these notes (@Cacheable、@CachePut and @CacheEvic) You can also specify the value pair of the attribute @CacheConfig Override the attribute values in .

@CacheConfig(cacheNames = "cache1")
public class ArticleService {
    @Cacheable(key = "'findById'+#id")
    public String findById(Long id) {
        this.articleMap.put(1L, "spring series ");
        System.out.println("---- Access to the article :" + id);
        return articleMap.get(id);
    }
}
 Copy code 

principle

spring The main purpose of caching in is to make use of spring in aop Realized , adopt Aop For those that need to use cache bean Create proxy object , Intercept the execution of the target method through the proxy object , Realize the cache function .

The point is @EnableCaching This annotation , It can be downloaded from @Import This note looks like

@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
}
 Copy code 

Eventually, it will be given to those who need to use cache bean Create proxy object , And an interceptor will be added to the agent org.springframework.cache.interceptor.CacheInterceptor, In this class invoke Method is the key , It will intercept the execution of all cache related target methods , You can take a closer look .

版权声明
本文为[Drum Tower]所创,转载请带上原文链接,感谢
https://cdmana.com/2022/134/202205141227435611.html

Scroll to Top