https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache
Integration
The Spring Framework provides abstractions for the asynchronous execution and scheduling of tasks with the TaskExecutor and TaskScheduler interfaces, respectively. Spring also features implementations of those interfaces that support thread pools or delega
docs.spring.io
틀린 해석이 있다면 알려주세요 🧃
6. Cache Abstraction ~ 6.2. Declarative Annotation-based Caching
6. Cache Abstraction
Spring Framework 는 Caching을 transparently하게 추가하는 지원을 제공한다
캐싱 추상화를 통해 코드에 미치는 영향을 최소화 시키고 캐싱 솔루션을 다양하고 일관되게 이용할 수 있다
6.1. Understanding the Cache Abstraction
버퍼는 빠른 엔티티와 느린 엔티티 사이의 데이터 중간 저장소. 데이터는 버퍼에서 한번만 쓰고 읽음.
캐시는 정의상 숨겨져 있고, 캐싱이 발생되는걸 알 수 없어야함. 동일한 데이터를 여러번 읽을 수 있음
https://en.wikipedia.org/wiki/Cache_(computing)#The_difference_between_buffer_and_cache
Cache (computing) - Wikipedia
From Wikipedia, the free encyclopedia Additional storage that enables faster access to main storage Diagram of a CPU memory cache operation In computing, a cache ( KASH)[1] is a hardware or software component that stores data so that future requests for th
en.wikipedia.org
핵심적으로 캐시 추상화는 Java 메서드에 캐싱을 적용하여 캐시에서 사용 가능한 정보를 기반으로 실행 횟수를 줄인다.
대상 메서드가 호출될 때마다 추상화는 해당 메서드가 지정된 인수에 대해 이미 호출되었는지 여부를 확인하는 캐싱 동작을 적용한다.
호출된 경우 실제 메서드를 호출하지 않고도 캐시된 결과가 반환시킨다. 메서드가 호출되지 않은 경우 메서드가 호출되고 결과가 캐시되어 사용자에게 반환되므로 다음에 메서드가 호출될 때 캐시된 결과가 반환되게 된다.
이렇게 하면 비용이 많이 드는 메서드(CPU 또는 IO 바인딩 여부)를 지정된 매개 변수 세트에 대해 한 번만 호출할 수 있으며 실제로 메서드를 다시 호출하지 않고도 결과를 재사용할 수 있다.
This approach works only for methods that are guaranteed to return the same output (result) for a given input (or arguments) no matter how many times they are invoked.
이러한 접근 방식은 호출 횟수와 상관없이 주어진 input에 대해 동일한 output이 보장된 메서드에서 제공된다
Cache Abstraction 은 캐시 contents를 업데이트하거나 하나 또는 모든 항목의 캐시를 제거하는 기능 등의 캐시 관련 작업을 제공한다
Caching Service는 Abstract 이고 캐시 데이터를 저장하기 위해 실제 스토리지를 사용해야한다.
This abstraction is materialized by the org.springframework.cache.Cache and org.springframework.cache.CacheManager interfaces.
** Caching Abstraction 에서는 multi thread 나 multi process 환경에 대한 특별 처리가없다 (캐시 구현에서 처리 됨)
multi-process environment(여러 노드에 배포된 응용 프로그램)이 있는 경우 그에 따라 캐시 공급자를 구성해야 한다. 사용 사례에 따라 여러 노드에 있는 동일한 데이터의 복사본이면 충분히 할 수 있다. 그러나 애플리케이션 과정 중에 데이터를 변경하는 경우 다른 전파 메커니즘을 활성화해야 할 수 있다.
particular item을 캐싱하는 것은 프로그래밍 방식의 캐시 상호 작용에서 발견되는 일반적인 get-if-not-found-then-proceed-and-put-eventually 코드 블록과 직접적으로 동일하다다. locks가 적용되지 않으며 여러 스레드가 동일한 항목을 동시에 로드하려고 시도할 수 있다. eviction도 마찬가지이다. 여러 스레드가 데이터를 동시에 업데이트하거나 제거하려고 하면 오래된 데이터를 사용할 수 있다. 특정 캐시 공급자는 해당 영역에서 고급 기능을 제공한다. (cache provider 참조)
캐시 추상화를 사용하려면 다음 두 가지 측면을 처리해야 한다.
- Caching declaration: 캐싱해야 하는 메서드와 해당 정책을 식별
- Cache configuration: 데이터가 저장되고 데이터를 읽는 백업 캐시
6.2. Declarative Annotation-based Caching
For caching declaration, Spring’s caching abstraction provides a set of Java annotations:
- @Cacheable: Triggers cache population.
- @CacheEvict: Triggers cache eviction.
- @CachePut: Updates the cache without interfering with the method execution.
- @Caching: Regroups multiple cache operations to be applied on a method.
- @CacheConfig: Shares some common cache-related settings at class-level.
6.2.1. The @Cacheable Annotation
cache 가능한 메서드를 구분할 때 사용한다 예시와 같이 연결할 캐시 이름이 필요하다
@Cacheable("books")
public Book findBook(ISBN isbn) {...}
메서드가 호출될 때마다 호출이 이미 실행되었고 반복할 필요가 없는지 확인하기 위해 캐시를 확인해준다.
대부분의 경우 하나의 캐시만 선언되지만 주석을 사용하면 둘 이상의 캐시가 사용되도록 여러 이름을 지정할 수도 있다.
이 경우 메서드를 호출하기 전에 각 캐시를 확인하고 하나 이상의 캐시에 적중하면 관련 값이 반환된다
Cached method는 실제 호출되지 않았더라도 값을 포함하지 않은 모든 캐시가 업데이트 된다
여러 캐시 메소드 사용 가능
@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}
Default Key Generation
캐시는 키-값 으로 된 저장소이므로 적합한 키로 변환되어야 한다
Since caches are essentially key-value stores, each invocation of a cached method needs to be translated into a suitable key for cache access.
The caching abstraction uses a simple KeyGenerator based on the following algorithm:
- If no parameters are given, return SimpleKey.EMPTY. // 매개변수 제공되지않으면 EMPTY 반환
- If only one parameter is given, return that instance.
- If more than one parameter is given, return a SimpleKey that contains all parameters.
This approach works well for most use-cases, as long as parameters have natural keys and implement valid hashCode() and equals() methods.
If that is not the case, you need to change the strategy. To provide a different default key generator, you need to implement the org.springframework.cache.interceptor.KeyGenerator interface.
* Spring 4.0 release 와 함께 변경되었다. 현재는 SimpleKeyGenerator 복합키를 사용한다.
https://github.com/spring-projects/spring-framework/issues/14870
Cacheable key collision with DefaultKeyGenerator [SPR-10237] · Issue #14870 · spring-projects/spring-framework
Morten Haraldsen opened SPR-10237 and commented The default is to use the hashcode of each parameter and create another (32-bit) hash code. Obviously this can easily generate collisions. This shoul...
github.com
이전 키 전략을 그대로 사용하려면 org.springframework.cache.interceptor.DefaultKeyGenerator 의 KeyGenerator 로 사용할 수 있다고 한다
Custom Key Generation Declaration
Caching은 일반적이기 때문에 캐시 구조 위에 쉽게 매핑할 수 없는 시그니쳐를 가질 가능성이 높다
대상 method에 여러인수가 있을 때 그중 일부만 캐싱할 경우를 보자
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
두 boolean 중에서 하나만 캐싱하려고 한다면 어떻게 해야할까?
그럴 경우에는 @Cacheable 애노테이션에 값을 더 추가해주면 된다
SpEL을 이용하여 작성하면 된다
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
key 생성 알고리즘을 좀더 자세히 쓰고 싶거나 공유가 필요할 경우에는 keyGenerator 에서 정의하면 된다
@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
The key and keyGenerator parameters are mutually exclusive and an operation that specifies both results in an exception.
지정 시에 key 와 keyGenerator 둘다 지정하려고 하면 예외처리된다
Default Cache Resolution
The caching abstraction uses a simple CacheResolver that retrieves the caches defined at the operation level by using the configured CacheManager.
간단하게는 Cachemanager를 사용하여 정의 된 캐시를 검색하려면 CacheResolver 를 사용하면 된다.
다른 기본 캐시 리졸버를 구성하려면 아래의 the org.springframework.cache.interceptor.CacheResolver 인터페이스를 구현해야한다.
Custom Cache Resolution
복잡하지 않은 애플리케이션에서의 Cache Resolution
@Cacheable(cacheNames="books", cacheManager="anotherCacheManager")
public Book findBook(ISBN isbn) {...}
키 생성 교체와 유사한 방식으로 CacheResolver를 완전히 교체할 수도 있다.
해석은 모든 캐시 작업에 대해 요청되므로 구현에서 런타임 인수를 기반으로 사용할 캐시를 실제로 확인할 수 있다.
@Cacheable(cacheResolver="runtimeCacheResolver")
public Book findBook(ISBN isbn) {...}
** Spring 4.1 부터 cache 애노테이션 값 속성은 필수 작성하지않아도 된다. 주석의 내용에 관계없이 CacheResolver에서 제공해줄 수 있다.
key 및 keyGenerator와 마찬가지로 cacheManager 및 cacheResolver 매개변수 또한 두 작업을 모두 지정하는 작업을 하면 CacheResolver 구현에서 사용자 지정 CacheManager를 무시하므로 예외가 발생한다
Synchronized Caching
multi-threaded environment 에서는 동일한 인수에 대해 특정 작업이 동시에 호출 될 수 있다. (보통 시작 시 발생) 기본적으로 cache abstrarction 은 아무것도 lock 하지 않으며 동일한 값이 여러번 계산되어서 caching의 목적이 무산될 수 있다
그럴 경우에는 sync 속성을 사용하여 중복 요청이 들어올 경우 해당 캐시 항목을 잠글 수 있도록 할 수 있다
@Cacheable(cacheNames="foos", sync=true)
public Foo executeExpensiveOperation(String id) {...}
- 선택적인 사항이라 지원하지않는 캐시라이브러리가 있을 수 있다
CacheManager는 지원한다.
Conditional Caching
조건에 따라 캐싱을 할지말지 정할 경우가 있을 수 있다. 이럴 경우 @Cacheable 애노테이션에서 parameter로 condition을 추가해주면 된다.
@Cacheable(cacheNames="book", condition="#name.length() < 32")
public Book findBook(String name)
unless를 이용해서 캐시 추가를 거부할 수도 있다.
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback")
public Book findBook(String name)
또한 Optional 타입 리턴이 가능하다. 값이 없으면 null로 저장된다.
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback")
public Optional<Book> findBook(String name)
Each SpEL expression evaluates against a dedicated context. In addition to the built-in parameters, the framework provides dedicated caching-related metadata, such as the argument names. The following table describes the items made available to the context so that you can use them for key and conditional computations:
methodName | Root object | The name of the method being invoked | #root.methodName |
method | Root object | The method being invoked | #root.method.name |
target | Root object | The target object being invoked | #root.target |
targetClass | Root object | The class of the target being invoked | #root.targetClass |
args | Root object | The arguments (as array) used for invoking the target | #root.args[0] |
caches | Root object | Collection of caches against which the current method is run | #root.caches[0].name |
Argument name | Evaluation context | Name of any of the method arguments. If the names are not available (perhaps due to having no debug information), the argument names are also available under the #a<#arg> where #arg stands for the argument index (starting from 0). | #iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias). |
result | Evaluation context | The result of the method call (the value to be cached). Only available in unless expressions, cache put expressions (to compute the key), or cache evict expressions (when beforeInvocation is false). For supported wrappers (such as Optional), #result refers to the actual object, not the wrapper. | #result |
6.2.2. The @CachePut Annotation
method 실행에 방해하지 않고 캐시를 업데이트해야할 경우에 사용된다
메소드는 무조건 호출되는데 그 결과가 캐시에 저장된다
@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
이는 @Cacheable 했을 때와 동일한 결과값을 기대하기 어렵다.
6.2.3. The @CacheEvict annotation
cache를 제거할 수 있다. 사용하지않을 경우 제거하는데 유용하다.
@Cacheable 과 다르게 이는 캐시 제거를 수행하는 method이기 때문에 작업할 캐시를 지정해주어야하고, 해당 캐시 및 키를 확인하는 조건을 지정할 수 있고 전체 제거 여부를 설정하는 allEntries를 제공한다
다 지워지게 하고 싶을 때
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)
만일 method가 실행되지 않거나 예외가 throw되면 제거는 발생하지 않는다
beforeInvocation를 true로 설정하면 호출 전에 캐싱을 제거할 수 있도록 한다
void method의 경우 @CacheEvict 처럼 사용이 가능하다
method 자체가 trigger 이기 때문에 반환 값이 무시된다
참고로, 캐시에 데이터를 추가하거나 업데이트를 하여 결과를 요구하는 @Cacheable 은 해당되지않는다
6.2.4. The @Caching Annotation
경우에 따라 동일한 타입(@CacheEvict, @CachePut) 등의 여러 주석을 지정해줘야할 경우가 있다. 어느 캐시 키에는 저장할건데 어디는 삭제할거고 이런, 경우에는 중첩해서 @Caching에 묶어서 사용하면 된다
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)
6.2.5. The @CacheConfig annotation
또한 모든 캐시 작업에 사용할 캐시 이름을 한 곳에 지정하여 단일 클래스 수준으로 정의를 대체할 수 있다.
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
}
클래스 자체에 @CacheConfig 설정하고 하위 내용에 캐싱 작업을 설정해준다
An operation-level customization always overrides a customization set on @CacheConfig. Therefore, this gives three levels of customizations for each cache operation:
- Globally configured, available for CacheManager, KeyGenerator.
- At the class level, using @CacheConfig.
- At the operation level.
6.2.6. Enabling Caching Annotations
@Caching 애노테이션을 붙인 뒤에는 꼭 @EnableCaching, @Configuration을 선언해줘야한다
@Configuration
@EnableCaching
public class AppConfig {
}
xml의 경우
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
</beans>
CachingConfigurer (Spring Framework 6.0.5 API)
Return the CacheResolver bean to use to resolve regular caches for annotation-driven cache management. This is an alternative and more powerful option of specifying the CacheManager to use. If both a cacheManager() and #cacheResolver() are set, the cache m
docs.spring.io
Cache annotation settingsXML AttributeAnnotation AttributeDefaultDescription
cache-manager | N/A (see the CachingConfigurer javadoc) | cacheManager | The name of the cache manager to use. A default CacheResolver is initialized behind the scenes with this cache manager (or cacheManager if not set). For more fine-grained management of the cache resolution, consider setting the 'cache-resolver' attribute. |
cache-resolver | N/A (see the CachingConfigurer javadoc) | A SimpleCacheResolver using the configured cacheManager. | The bean name of the CacheResolver that is to be used to resolve the backing caches. This attribute is not required and needs to be specified only as an alternative to the 'cache-manager' attribute. |
key-generator | N/A (see the CachingConfigurer javadoc) | SimpleKeyGenerator | Name of the custom key generator to use. |
error-handler | N/A (see the CachingConfigurer javadoc) | SimpleCacheErrorHandler | The name of the custom cache error handler to use. By default, any exception thrown during a cache related operation is thrown back at the client. |
mode | mode | proxy | The default mode (proxy) processes annotated beans to be proxied by using Spring’s AOP framework (following proxy semantics, as discussed earlier, applying to method calls coming in through the proxy only). The alternative mode (aspectj) instead weaves the affected classes with Spring’s AspectJ caching aspect, modifying the target class byte code to apply to any kind of method call. AspectJ weaving requires spring-aspects.jar in the classpath as well as load-time weaving (or compile-time weaving) enabled. (See Spring configuration for details on how to set up load-time weaving.) |
proxy-target-class | proxyTargetClass | false | Applies to proxy mode only. Controls what type of caching proxies are created for classes annotated with the @Cacheable or @CacheEvict annotations. If the proxy-target-class attribute is set to true, class-based proxies are created. If proxy-target-class is false or if the attribute is omitted, standard JDK interface-based proxies are created. (See Proxying Mechanisms for a detailed examination of the different proxy types.) |
order | order | Ordered.LOWEST_PRECEDENCE | Defines the order of the cache advice that is applied to beans annotated with @Cacheable or @CacheEvict. (For more information about the rules related to ordering AOP advice, see Advice Ordering.) No specified ordering means that the AOP subsystem determines the order of the advice. |
Method visibility and cache annotations
proxy를 사용하려면 꼭 visibility 한 메소드에 적용해야한다
If you do annotate protected, private, or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider using AspectJ (see the rest of this section) if you need to annotate non-public methods, as it changes the bytecode itself.
Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Cache* annotations, as opposed to annotating interfaces. You certainly can place an @Cache* annotation on an interface (or an interface method), but this works only if you use the proxy mode (mode="proxy"). If you use the weaving-based aspect (mode="aspectj"), the caching settings are not recognized on interface-level declarations by the weaving infrastructure.
프록시 모드(기본값)에서는 프록시를 통해 들어오는 외부 메서드 호출만 가로챈다.
즉, 자체 호출(실제로 대상 개체의 다른 메서드를 호출하는 대상 개체 내의 메서드)은 호출된 메서드가 @Cacheable로 표시되어 있어도 런타임 시 실제 캐싱으로 이어지지 않는다. 이 경우 aspectj 모드를 사용하는 것이 좋다.
또한 예상되는 동작을 제공하려면 프록시를 완전히 초기화해야 하므로 초기화 코드(즉, @PostConstruct)에서 이 기능에 의존해서는 안 된다.
6.2.7. Using Custom Annotations
proxy 기반 접근 방식에서 작동하지만 AspectJ를 사용하여 활성화 할 수 있다
The spring-aspects module defines an aspect for the standard annotations only. If you have defined your own annotations, you also need to define an aspect for those.
The caching abstraction lets you use your own annotations to identify what method triggers cache population or eviction. This is quite handy as a template mechanism, as it eliminates the need to duplicate cache annotation declarations, which is especially useful if the key or condition are specified or if the foreign imports (org.springframework) are not allowed in your code base. Similarly to the rest of the stereotype annotations, you can use @Cacheable, @CachePut, @CacheEvict, and @CacheConfig as meta-annotations (that is, annotations that can annotate other annotations). In the following example, we replace a common @Cacheable declaration with our own custom annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames="books", key="#isbn")
public @interface SlowService {
}
In the preceding example, we have defined our own SlowService annotation, which itself is annotated with @Cacheable. Now we can replace the following code:
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
The following example shows the custom annotation with which we can replace the preceding code:
@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Even though @SlowService is not a Spring annotation, the container automatically picks up its declaration at runtime and understands its meaning. Note that, as mentioned earlier, annotation-driven behavior needs to be enabled.
'Web > spring' 카테고리의 다른 글
[Spring Boot IO] 1. Caching (0) | 2023.02.28 |
---|---|
[Spring Framework integration] 6.Cache Abstraction (2) (0) | 2023.02.28 |
[Spring REST] Setting up Your Tests (0) | 2023.02.26 |
[Spring data] JPA repositories - Projection (0) | 2023.02.26 |
[Spring Framework Core] 4.3. Language Reference (3) (0) | 2023.02.24 |