====== Cache Abstraction ====== ===== 개요 ===== 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 === * [[eGovframework:RTE2:FDL:EhCache]] 참조 Spring에서는 EhCache를 지원하는 CacheManager로써 EhCacheCacheManager를 제공한다. Ehcache.xml에서 defaultCache저장소와 추가 Cache저장소의 설정을 한다. EhCAche는 비록 ConcurrentMap보다 속도는 느리지만 Cache관리기능 측면에서 유용하여 EhCache를 추천한다. * 프로젝트에서는 Cache의 사용에 대하여 개발자의 가이드가 필요하다. 업무별/서비스별 Cache저장소를 분리하여 Cache데이터를 관리하도록 하며, 적용케이스에 대하여 가이드하여야 한다. === ConcurrentMap === Spring에서는 ConcurrentMap을 지원하는 SimpleCacheManager를 제공한다. === Cache Annotation을 위한 annotation-driven 설정 === Cache Annotation으로 Cache를 적용하기 위해서는 반드시 을 붙여주어야한다. 이 네임스페이스는 AOP를 사용해서 캐시 기능을 다양한 방법으로 설정할 수 있는 옵션을 제공한다. 이 설정은 Transaction에 사용되는 과 비슷하다. ^ 속성 ^ 기본값 ^ 설명 ^ | 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가 적용되는 순서 | * 은 오로지 이것이 정의된 동일 ApplicationContext안의 bean에서 @Cacheable과 @CacheEvict을 찾는다. 즉, 을 DispatcherServlet을 위한 WebApplicationContext에 선언했을때 @Cacheable/@CacheEvict는 controller내부에서만 식별된다. ==== CacheManager를 통한 Cache접근 ==== ehCache, concurrentMap의 설정과 상관없이 코드상으로 동일한 CacheManager인터페이스로 bean을 주입받아 사용할 수 있다. 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에 메소드 데이터를 삭제한다. === @Cacheable 사용방법 === 별다른 조건없이 호출되는 모든 인자를 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 Abstraction Key생성 == Cache는 키-값으로 저장되며, 캐시된 메소드를 호출시마다 키를 통해 값을 가져오므로 캐시를 찾을 수 있는 키가 생성되어야한다. 별도의 커스텀키가 정의되지 않으면 default로 다음과 같은 알고리즘 기반의 KeyGenerator를 사용하여 Key를 생성한다. * 매개변수가 아무것도 없으면 0을 반환한다. * 매개변수가 하나면, 그 인스턴스를 반환한다. * 매개변수가 둘 이상이면, 모든 매개변수의 Hash로 계산된 키를 반환한다. 이 외에 다른 기본키를 생성하려면 org.springframework.cache.KeyGenerator 인터페이스를 구현하면 된다. == Custom key 추가 == @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 추가 == 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) ==== XML기반 Caching설정 ==== Java메소드에 Annotation을 붙이는 대신 XML로 caching할 메소드를 정의할 수 있다. 아래에서는 CacheManager bean설정을 생략하였다. BookService패키지의 하위메소드 중 findBook 메소드에 cacheable이 적용되며 loadBooks 메소드에 cacheEvict가 적용되었다. ... // CacheManager설정 생략 ===== 참고자료 ===== * [[http://docs.spring.io/spring/docs/3.2.6.RELEASE/spring-framework-reference/htmlsingle/#cache#beans-dependencies|Spring Framework - Reference Document / cache]] * [[http://spring.io/blog/2011/02/23/spring-3-1-m1-caching/|Spring Team Blog - Caching]]