개요
Spring 3.1부터 Cache Service는 Cache 추상화(CacheManager Interface)와 Cache 추상화를 Java메소드에 제공할 수 있는 @Cacheable을 제공한다. Cache 추상화는 Spring의 트랜잭션기능과 유사하게 코드의 변화를 최소화하면서 Proxy를 통해 동작하게끔 한다. Cache 구현체가 아닌 Cache추상화만을 제공하며 실제 Cache Data저장소는 EhCache와 ConcurrentMap을 지원한다.
-
Cache Configuration : Cache설정을 통하여 어떠한 Cache Data저장소를 쓸 것인지 결정할 수 있다. (EhCache/ConcurrentMap)
-
Cache Manager : CacheManager를 통해 설정과 상관없이 동일한 코드로 Cache에 접근할 수 있다.
-
Cache Annotation : 메소드의 Cache Annotation을 통해 쉽게 Cache데이터를 저장/삭제할 수 있다.
설명
Cache를 설정하여 CacheManager를 통해 Cache에 접근하는 방법에 대하여 알아보고, 자바메소드를 Caching하는 @Cacheable에 대하여 알아본다.
Cache Configuration
-
EhCache 참조
Spring에서는 EhCache를 지원하는 CacheManager로써 EhCacheCacheManager를 제공한다.
<EhCache 설정>
<cache:annotation-driven cache-manager="cacheManager" />
<!-- EhCache를 저장소로 사용하는 Cache Manager -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache"></property>
</bean>
<!-- Ehcache library setup -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:config-location="classpath:springframework/cache/ehcache/ehcache.xml" />
Ehcache.xml에서 defaultCache저장소와 추가 Cache저장소의 설정을 한다. EhCAche는 비록 ConcurrentMap보다 속도는 느리지만 Cache관리기능 측면에서 유용하여 EhCache를 추천한다.
-
프로젝트에서는 Cache의 사용에 대하여 개발자의 가이드가 필요하다. 업무별/서비스별 Cache저장소를 분리하여 Cache데이터를 관리하도록 하며, 적용케이스에 대하여 가이드하여야 한다.
<ehcache.xml>
<ehcache…>
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="false" />
<cache name="ehcache" …>
Spring에서는 ConcurrentMap을 지원하는 SimpleCacheManager를 제공한다.
<CacheManager설정>
<cache:annotation-driven cache-manager="cacheManager"/>
<!-- ConcurrentMap을 저장소로 사용하는 Cache Manager -->
<bean id="cacheManager"
class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="default"/>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="books"/> </set> </property> </bean>
Cache Annotation을 위한 annotation-driven 설정
Cache Annotation으로 Cache를 적용하기 위해서는 반드시 <cache:annotatioin-driven>을 붙여주어야한다. 이 네임스페이스는 AOP를 사용해서 캐시 기능을 다양한 방법으로 설정할 수 있는 옵션을 제공한다. 이 설정은 Transaction에 사용되는 <tx:annotation-driven>과 비슷하다.
속성기본값설명
cache-manager | cacheManager | 사용할 CacheManager의 이름. CacheManager의 Bean id가 cacheManager가 아닐 경우, 설정해야한다. |
mode | proxy | 스프링 AOP를 사용하는 설정이며, “aspect”를 사용할 수도 있다. |
proxy-target-class | false | false인 경우, JDK의 인터페이스 기반 프록시를 사용한다. true를 사용하면 클래스 기반 프록시를 사용한다. |
order | Ordered.LOWEST_PRECEDENCE | @Cacheable/@CacheEvict메소드의 Cache advice가 적용되는 순서 |
-
<cache:annotation-driven/>은 오로지 이것이 정의된 동일 ApplicationContext안의 bean에서 @Cacheable과 @CacheEvict을 찾는다. 즉, <cache:annotation-driven>을 DispatcherServlet을 위한 WebApplicationContext에 선언했을때 @Cacheable/@CacheEvict는 controller내부에서만 식별된다.
CacheManager를 통한 Cache접근
ehCache, concurrentMap의 설정과 상관없이 코드상으로 동일한 CacheManager인터페이스로 bean을 주입받아 사용할 수 있다.
<CacheManager 의 사용>
import org.springframework.cache.CacheManager;
@Autowired private CacheManager cacheManager;
Cache cache = cacheManager.getCache("cache명");
@Cacheable
자바메소드에 @Cacheable을 설정함으로써 Caching할 수 있다. 타겟메소드가 호출되었을 때, 캐시에 해당 메서드가 이미 동일한 인자로 있는지 확인하고, 만약 있다면 메소드를 호출하지 않고 캐시해둔 결과를 Proxy에서 반환하게 된다.
Java Method에 적용가능한 Cache Annotation은 다음과 같다.
-
@Cacheable : Cache에 메소드 데이터를 생성한다.
-
@CacheEvict : Cache에 메소드 데이터를 삭제한다.
별다른 조건없이 호출되는 모든 인자를 caching하고자 할 때는 아래와 같이 cache명만 쓰면 된다. 이 코드는 Cache이름이 “books”인 cache저장소를 사용하였다. 메소드가 호출될 때 매번 “books”에 Cache데이터를 확인하고 이미 실행된 적이 있는지를 확인한다. 만약 “books”에 데이터가 있으면 그 값을 반환하게 된다.
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Cache되는 저장소를 여러개 정의할 수도 있다. 아래의 코드에서는 findBook메소드 호출 시 “books”와 “isbns” 두 군데에 캐시데이터가 저장된다.
<여러 cache 저장소에 caching>
@Cacheable({ "books", "isbns" }) public Book findBook(ISBN isbn) {...}
Cache는 키-값으로 저장되며, 캐시된 메소드를 호출시마다 키를 통해 값을 가져오므로 캐시를 찾을 수 있는 키가 생성되어야한다. 별도의 커스텀키가 정의되지 않으면 default로 다음과 같은 알고리즘 기반의 KeyGenerator를 사용하여 Key를 생성한다.
-
매개변수가 아무것도 없으면 0을 반환한다.
-
매개변수가 하나면, 그 인스턴스를 반환한다.
-
매개변수가 둘 이상이면, 모든 매개변수의 Hash로 계산된 키를 반환한다.
이 외에 다른 기본키를 생성하려면 org.springframework.cache.KeyGenerator 인터페이스를 구현하면 된다.
@Cacheable을 적용한 메소드의 인자가 여러개일 때 Key로 사용할 것을 SpEL로 명시할 수 있다.
@Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(value="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Conditional을 주어서 그 값이 true이면 caching을 하고, false이면 caching을 하지 않기 때문에 호출시 매번 메소드내부가 실행된다. Conditional에서는 SpEL사용이 가능하며 Condition과 Unless를 쓸 수 있다. condition과는 달리 unless는 메소드의 결과값 반환 시점에 결과값을 확인하여 caching여부를 결정하게 된다.
@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)
@Cacheable(value="book", condition="#name.length < 32", unless="#result.hardback")
public Book findBook(String name)
Conditional에 쓰는 SpEL의 설명은 다음과 같다.
명칭위치설명예제
methodName | root객체 | 호출되는 메소드명 | #root.methodName |
method | root객체 | 호출되는 메소드 | #root.method.name |
target | root객체 | 호출되는 타겟오브젝트 | #root.target |
targetClass | root객체 | 호출되는 타겟 클래스 | #root.targetClass |
args | root객체 | 타겟을 호출 시 사용되는 인자들(배열) | #root.args[0] |
caches | root객체 | 현재 메소드가 실행되는 캐시들의 집합 | #root.caches[0].name |
argument name | 평가 context | 메소드 인자명을 사용할 수 없을 때 대신 a<#arg>로 대체하여 사용할 수 있다. #arg는 0부터 시작하는 인자의 인덱스를 나타난다. | iban 또는 a0 (p<#arg>로도 사용가능) |
result | 평가 context | 메소드호출 결과. unless와 cache evict표현에서만 사용가능하다. | #result |
@CacheEvict
@CacheEvict는 @Cacheable과 반대로 cache저장소의 데이터를 제거함으로써 사용하지 않는 데이터를 정리하는데 유용하다. @CacheEvict는 캐시삭제를 수행할 메서드에 선언한다. @CacheEvict도 여러개의 캐시를 명시할 수 있으며, key와 condition을 사용할 수 있다. 또한 allEntries속성은 키값으로 Cache Entrie하나만 비우는 것이 아니라 캐시영역의 모든 Entrie를 비우도록 한다. 이 경우에는 키를 명시하더라도 이를 무시하고 모든 Entrie를 비우게 된다.
@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch)
@CachePut
메소드의 흐름을 방해하지 않고 Cache에 저장하거나 업데이트를 해야하는 경우, @CachePut을 사용한다. 즉, 메소드는 항상 실행되고 그 결과가 캐시에 저장된다. @CachePut은 @Cacheable과 동일한 옵션을 제공하며, Cache에 저장하는것보다는 메소드의 흐름을 최적화하는데 사용되어야 한다.
@Cacheable과 함께 사용하는것은 일반적으로 권장하지 않는다.
@Caching
다수의 Cache annotation을 쓰고자 할 때 @Caching을 쓴다. @Cacheable, @CacheEvic, @CachePut을 지원한다.
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") })
public Book importBooks(String deposit, Date date)
참고 링크
www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte2:fdl:cache_abstraction
'스프링, 자바' 카테고리의 다른 글
Lombok (0) | 2020.10.04 |
---|---|
스프링 도큐먼트 (0) | 2020.10.03 |
스프링 캐쉬 사용법 (0) | 2020.10.03 |
BaseEntity.java (0) | 2020.10.03 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 50회차 미션 (0) | 2020.09.28 |