목차
캐싱 및 값 획득을 위해 @Cacheable 사용
1. @Cacheable의 역할
2. 공통 속성에 대한 설명
@Cacheable 주석을 메서드에 사용하면 해당 메서드에서 반환한 결과를 캐시할 수 있다는 의미입니다. 즉, 이 메소드의 반환 결과가 캐시에 저장되므로, 향후 동일한 매개변수로 메소드를 호출할 때 실제로 메소드를 실행하지 않고 캐시에 있는 값이 반환됩니다.
4. cacheManager & cacheResolver
5. sync
6. condition
7. unless
8. condition VS unless ?
Java java지도 시간 SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

May 20, 2023 pm 01:30 PM
springboot @cacheable

캐싱 및 값 획득을 위해 @Cacheable 사용

1. @Cacheable의 역할

캐싱을 사용하는 단계는 @Cacheable 주석을 사용하여 캐싱을 구현하는 것입니다. 따라서 먼저 캐시를 사용하는 단계에 대해 이야기할 수 있습니다.

주석 기반 캐싱을 활성화하려면 SpringBoot의 기본 시작 클래스에서 @EnableCaching 주석을 사용하세요.

캐시 주석을 표시하세요

1단계: 주석 기반 캐싱을 활성화하고 @EnableCaching을 사용하여 springboot 기본 시작 클래스에 표시합니다

//开启基于注解的缓存
@EnableCaching   
@EnableRyFeignClients
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ZfjgAuthApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZfjgAuthApplication.class, args);
    }
}
로그인 후 복사

2단계: 캐시 주석을 표시합니다

@Repository
public interface DeviceMapper {
    @Cacheable(cacheNames = "DeviceDO.deviceId")
    DeviceDO get(String deviceId);
    @CacheEvict(cacheNames = "DeviceDO.deviceId", key = "#record.deviceId")
    int insert(DeviceDO record);
}
로그인 후 복사

참고: @Cacheable은 여기서 사용되는 주석은 실행 결과를 캐시할 수 있으며, 향후 동일한 데이터를 쿼리할 경우 메소드 호출 없이 캐시에서 직접 가져올 수 있습니다.

2. 공통 속성에 대한 설명

다음은 @Cacheable 주석의 일반적으로 사용되는 여러 속성에 대한 소개입니다.

  • cacheNames/value: 사용 캐시 구성 요소 지정 이름 cacheNames/value:用来指定缓存组件的名字

  • key:缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)

  • keyGenerator:key 的生成器。 key 和 keyGenerator 二选一使用

  • cacheManager:可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。

  • condition:可以用来指定符合条件的情况下才缓存

  • unless:否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)

  • sync

key: 데이터를 캐싱할 때 사용되는 키를 지정하는 데 사용할 수 있습니다. 기본값은 메소드 매개변수의 값을 사용하는 것입니다. (spEL 표현식을 사용하여 이 키를 작성할 수 있습니다.)

keyGenerator: 키 생성기. 키 또는 keyGenerator

cacheManager 사용: 캐시 관리자를 지정하는 데 사용할 수 있습니다. 캐시를 얻을 캐시 관리자.

condition: 조건이 충족되는 경우에만 캐싱을 지정하는 데 사용할 수 있습니다.

uns: 캐싱을 무효화합니다. Except로 지정된 조건이 true인 경우 메서드의 반환 값은 캐시되지 않습니다. 물론, 판정 결과도 얻을 수 있습니다. (#result를 통해 메소드 결과 가져오기)

sync: 비동기 모드 사용 여부.

SpringBoot에서 Cacheable을 사용하기 위한 지침

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법함수 설명

@Cacheable 주석을 메서드에 사용하면 해당 메서드에서 반환한 결과를 캐시할 수 있다는 의미입니다. 즉, 이 메소드의 반환 결과가 캐시에 저장되므로, 향후 동일한 매개변수로 메소드를 호출할 때 실제로 메소드를 실행하지 않고 캐시에 있는 값이 반환됩니다.

여기서 한 가지 강조된 점은 매개변수가 동일하다는 것입니다. 캐시는 메소드의 실행 논리에 관심이 없기 때문에 이해하기 쉽습니다. 캐시가 결정할 수 있는 것은 동일한 메소드에 대해 매개변수가 동일하면 반환 결과도 동일하다는 것입니다. 그러나 매개변수가 다른 경우 캐시는 결과가 다르다고 가정할 수 있으므로 동일한 메소드에 대해 프로그램 실행 중에 메소드를 호출하는 데 사용되는 매개변수 조합 수, 이론적으로 캐시된 키 수는 몇 개 생성됩니까( 물론 이러한 조합의 매개변수는 생성된 키를 참조합니다. @Cacheable의 일부 매개변수를 살펴보겠습니다.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};
    String key() default "";
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
    String condition() default "";
    String unless() default "";
    boolean sync() default false;
}
로그인 후 복사

1.cacheNames & value값 또는 캐시 이름 중 하나를 사용하여 @Cacheable을 통해 얻을 수 있는 캐시 이름을 지정할 수 있습니다. @Cacheable의 가장 간단한 사용 예는 다음과 같습니다.

@Override
@Cacheable("menu")
public Menu findById(String id) {
    Menu menu = this.getById(id);
    if (menu != null){
        System.out.println("menu.name = " + menu.getName());
    }
    return menu;
}
로그인 후 복사

이 예에서 메뉴 캐시는 findById라는 메서드와 연결됩니다. 메뉴 캐시에 이미 결과가 있는 경우 이 메소드를 호출하면 실행되지 않고, 캐시된 결과가 바로 사용됩니다.

    2. 여러 캐시 이름 연결
  • 사실 공식 문서에 따르면 @Cacheable은 여러 캐시를 연결하는 동일한 방법을 지원합니다. 이 경우 메서드를 실행하기 전에 연결된 각 캐시를 확인하고

    캐시 중 하나 이상이 적중
  • 하는 한 이 캐시의 값이 반환됩니다.
  • 예:

    @Override
        @Cacheable({"menu", "menuById"})
        public Menu findById(String id) {
            Menu menu = this.getById(id);
            if (menu != null){
                System.out.println("menu.name = " + menu.getName());
            }
            return menu;
        }
    ---------
    @GetMapping("/findById/{id}")
    public Menu findById(@PathVariable("id")String id){
        Menu menu0 = menuService.findById("fe278df654adf23cf6687f64d1549c0a");
        Menu menu2 = menuService.findById("fb6106721f289ebf0969565fa8361c75");
        return menu0;
    }
    로그인 후 복사
  • 직관을 위해 id 매개변수를 코드에 직접 작성하세요. 이제 테스트하고 결과를 살펴보겠습니다.
3. key & keyGenerator

캐시 이름은 주석이 달린 메소드에 해당하지만 메소드는 다른 매개변수를 전달할 수 있으며 결과는 어떻게 달라집니다. 구별되다? 이를 위해서는 key 를 사용해야 합니다. Spring 프레임워크에서는 키 값을 명시적으로 지정하는 방법과 키 생성기를 사용하여 자동으로 생성하는 두 가지 방법으로 생성할 수 있습니다.

3.1 KeyGenerator 자동 생성

🎜🎜키 매개변수를 지정하지 않고 @Cacheable을 선언하면 캐시 이름 아래의 모든 키가 매개변수를 기반으로 KeyGenerator를 사용하여 자동으로 생성됩니다. Spring에는 기본 SimpleKeyGenerator가 있으며 이는 스프링 부트 자동화 구성에 기본적으로 주입됩니다. 생성 규칙은 다음과 같습니다. 🎜🎜🎜🎜캐시 메서드에 매개변수가 없으면 SimpleKey.EMPTY를 반환하고, 🎜🎜🎜🎜캐시 메서드에 매개변수가 하나 있으면 매개변수의 인스턴스를 반환합니다. 메소드에는 여러 매개변수가 있으며, 모든 매개변수를 포함하는 SimpleKey를 반환합니다. 🎜🎜🎜🎜기본 키 생성기에서는 매개변수에 hashCode() 및 equals() 메소드의 유효한 구현이 필요합니다. 또는 keyGenerator를 사용자 정의하고 이를 통해 지정할 수 있습니다. 여기서는 KeyGenerator에 대한 자세한 소개를 하지 않겠습니다. 실제로 KeyGenerator는 hashCode를 사용하여 덧셈과 곱셈 연산을 수행합니다. String 및 ArrayList에 대한 해시 계산과 유사합니다. 🎜🎜🎜3.2 키를 명시적으로 지정🎜🎜

相较于使用 KeyGenerator 生成,spring 官方更推荐显式指定 key 的方式,即指定 @Cacheable 的 key 参数。

即便是显式指定,但是 key 的值还是需要根据参数的不同来生成,那么如何实现动态拼接呢?SpEL(Spring Expression Language,Spring 表达式语言) 能做到这一点。下面是一些使用 SpEL 生成 key 的例子。

@Override
    @Cacheable(value = {"menuById"}, key = "#id")
    public Menu findById(String id) {
        Menu menu = this.getById(id);
        if (menu != null){
            System.out.println("menu.name = " + menu.getName());
        }
        return menu;
    }
    @Override
    @Cacheable(value = {"menuById"}, key = "'id-' + #menu.id")
    public Menu findById(Menu menu) {
        return menu;
    }
    @Override
    @Cacheable(value = {"menuById"}, key = "'hash' + #menu.hashCode()")
    public Menu findByHash(Menu menu) {
        return menu;
    }
로그인 후 복사

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

官方说 key 和 keyGenerator 参数是互斥的,同时指定两个会导致异常。

4. cacheManager & cacheResolver

缓存管理器CacheManager用于管理(寻找)某种缓存。通常来讲,缓存管理器是与缓存组件类型相关联的。我们知道,spring 缓存抽象的目的是为使用不同缓存组件类型提供统一的访问接口,以向开发者屏蔽各种缓存组件的差异性。那么 CacheManager 就是承担了这种屏蔽的功能。spring 为其支持的每一种缓存的组件类型提供了一个默认的 manager,如:RedisCacheManager 管理 redis 相关的缓存的检索、EhCacheManager 管理 ehCache 相关的缓等。

CacheResolver,缓存解析器是用来管理缓存管理器的,CacheResolver 保持一个 cacheManager 的引用,并通过它来检索缓存。CacheResolver 与 CacheManager 的关系有点类似于 KeyGenerator 跟 key。spring 默认提供了一个 SimpleCacheResolver,开发者可以自定义并通过 @Bean 来注入自定义的解析器,以实现更灵活的检索。

大多数情况下,我们的系统只会配置一种缓存,所以我们并不需要显式指定 cacheManager 或者 cacheResolver。Spring允许我们在系统中配置多个缓存组件,这时我们需要进行明确的指定。指定的方式是使用 @Cacheable 的 cacheManager 或者 cacheResolver 参数。

按照官方文档,cacheManager 和 cacheResolver 是互斥参数,同时指定两个可能会导致异常。

5. sync

是否同步,true/false。在一个多线程的环境中,某些操作可能被相同的参数并发地调用,这样同一个 value 值可能被多次计算(或多次访问 db),这样就达不到缓存的目的。针对这些可能高并发的操作,我们可以使用 sync 参数来告诉底层的缓存提供者将缓存的入口锁住,这样就只能有一个线程计算操作的结果值,而其它线程需要等待,这样就避免了 n-1 次数据库访问。

sync = true 可以有效的避免缓存击穿的问题。

6. condition

调用前判断,缓存的条件。有时候,我们可能并不想对一个方法的所有调用情况进行缓存,我们可能想要根据调用方法时候的某些参数值,来确定是否需要将结果进行缓存或者从缓存中取结果。例如,当我查询用户时按年龄分组,我只需缓存那些年龄大于 35 的结果。那么 condition 能实现这种效果。

SpEL 支持的表达式可以作为 condition 的值,结果为 true 或 false。如果表达式结果为 true,则调用方法时会执行正常的缓存逻辑(查缓存-有就返回-没有就执行方法-方法返回不空就添加缓存);否则,调用方法时就好像该方法没有声明缓存一样(即无论传入了什么参数或者缓存中有些什么值,都会执行方法,并且结果不放入缓存)。下面举个例子:

我们看一下数据库,以这两条数据为例:

  SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

我们首先定义一个带条件的缓存方法:

@Override
    @Cacheable(value = {"menuById"}, key = "#id", condition = "#conditionValue > 1")
    public Menu findById(String id, Integer conditionValue) {
        Menu menu = this.getById(id);
        if (menu != null){
            System.out.println("menu.name = " + menu.getName());
        }
        return menu;
    }
로그인 후 복사

然后分两种情况调用(为了直观可见,直接将 id 写在代码中):

@GetMapping("/findById/{id}")
    public Menu findById(@PathVariable("id")String id){
        Menu menu0 = menuService.findById("fe278df654adf23cf6687f64d1549c0a", 0);
        Menu menu2 = menuService.findById("fb6106721f289ebf0969565fa8361c75", 2);
        return menu0;
    }
로그인 후 복사

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

虽然两次请求都调用了方法,但只有第二次请求缓存了"微服务测试2"。只有在满足条件 condition 的情况下才会进行调用,这样才能将结果缓存。接下来我们再请求一遍,看下结果和打印:

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

可以看到,“微服务测试2”由于已经有了缓存,所以没有再执行方法体。而“微服务测试0”又一次执行了。

7. unless

执行后判断,不缓存的条件。SpEL 可以支持表达式结果为 true 或 false,用于 unless。当结果为 true 时,不缓存。举个例子:

我们先清除 redis 中的数据。然后看看 mysql 中的数据:

  SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

然后编写一个缓存方法(在该方法中,result代表方法的返回值。关于):

@Override
    @Cacheable(value = {"menuById"}, key = "#id", unless = "#result.type == 'folder'")
    public Menu findById(String id) {
        Menu menu = this.getById(id);
        if (menu != null){
            System.out.println("menu.name = " + menu.getName());
        }
        return menu;
    }
로그인 후 복사

然后调用该方法:

@GetMapping("/findById/{id}")
    public Menu findById(@PathVariable("id")String id){
        Menu menu0 = menuService.findById("fe278df654adf23cf6687f64d1549c0a");
        Menu menu2 = menuService.findById("fb6106721f289ebf0969565fa8361c75");
        return menu0;
    }
로그인 후 복사

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

可以看到,两次都执行了方法体(其实,unless 条件就是在方法执行完毕后调用,所以它不会影响方法的执行),但是结果只有 menu.type = ‘page’ 的缓存了,说明 unless 参数生效了。

8. condition VS unless ?

既然 condition 和 unless 都能决定是否进行缓存,那么同时指定这两个参数并且结果相冲突的时候,会怎么样呢?我们来试一试。

首先清除 redis 数据,然后在缓存方法上加上 condition=“true”,如:

@Override
    @Cacheable(value = {"menuById"}, key = "#id", condition = "true", unless = "#result.type == 'folder'")
    public Menu findById(String id) {
        Menu menu = this.getById(id);
        if (menu != null){
            System.out.println("menu.name = " + menu.getName());
        }
        return menu;
    }
로그인 후 복사

其它代码不变,我们来看一下缓存结果和打印:

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

可以看到,虽然两次调用都执行了,但是,type=‘folder’ 的还是被排除了。在这种情况下,unless 的优先级高于 condition。接着,我们将condition设为“false”,再进行尝试,结果是:

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법

可以看到,两次调用的结果都没有缓存。在这种情况下,优先使用condition而不是unless。总结起来就是:

  • condition 不指定相当于 true,unless 不指定相当于 false

  • condition = false,一定不会缓存;

  • condition = true,且 unless = true,不缓存;

  • condition = true,且 unless = false,缓存;

위 내용은 SpringBoot가 캐싱 및 값 검색을 위해 @Cacheable을 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

뜨거운 기사 태그

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Springboot가 Jasypt를 통합하여 구성 파일 암호화를 구현하는 방법 Springboot가 Jasypt를 통합하여 구성 파일 암호화를 구현하는 방법 Jun 01, 2023 am 08:55 AM

Springboot가 Jasypt를 통합하여 구성 파일 암호화를 구현하는 방법

SpringBoot가 Redisson을 통합하여 지연 대기열을 구현하는 방법 SpringBoot가 Redisson을 통합하여 지연 대기열을 구현하는 방법 May 30, 2023 pm 02:40 PM

SpringBoot가 Redisson을 통합하여 지연 대기열을 구현하는 방법

Redis를 사용하여 SpringBoot에서 분산 잠금을 구현하는 방법 Redis를 사용하여 SpringBoot에서 분산 잠금을 구현하는 방법 Jun 03, 2023 am 08:16 AM

Redis를 사용하여 SpringBoot에서 분산 잠금을 구현하는 방법

springboot가 파일을 jar 패키지로 읽은 후 파일에 액세스할 수 없는 문제를 해결하는 방법 springboot가 파일을 jar 패키지로 읽은 후 파일에 액세스할 수 없는 문제를 해결하는 방법 Jun 03, 2023 pm 04:38 PM

springboot가 파일을 jar 패키지로 읽은 후 파일에 액세스할 수 없는 문제를 해결하는 방법

SpringBoot와 SpringMVC의 비교 및 ​​차이점 분석 SpringBoot와 SpringMVC의 비교 및 ​​차이점 분석 Dec 29, 2023 am 11:02 AM

SpringBoot와 SpringMVC의 비교 및 ​​차이점 분석

SpringBoot가 Redis를 사용자 정의하여 캐시 직렬화를 구현하는 방법 SpringBoot가 Redis를 사용자 정의하여 캐시 직렬화를 구현하는 방법 Jun 03, 2023 am 11:32 AM

SpringBoot가 Redis를 사용자 정의하여 캐시 직렬화를 구현하는 방법

springboot에서 application.yml의 값을 얻는 방법 springboot에서 application.yml의 값을 얻는 방법 Jun 03, 2023 pm 06:43 PM

springboot에서 application.yml의 값을 얻는 방법

여러 테이블을 추가하기 위해 SQL 문을 사용하지 않고 Springboot+Mybatis-plus를 구현하는 방법 여러 테이블을 추가하기 위해 SQL 문을 사용하지 않고 Springboot+Mybatis-plus를 구현하는 방법 Jun 02, 2023 am 11:07 AM

여러 테이블을 추가하기 위해 SQL 문을 사용하지 않고 Springboot+Mybatis-plus를 구현하는 방법

See all articles