본문 바로가기

JAVA/Spring

캐싱

반응형

캐싱

  • java.util.concurrent.ConcurrentMap, Ehcache, Guava, GemFire나 JSR 107(자바 임시 캐싱 API, JCACHE )을 구현한 캐시 솔루션에 대한 캐시 추상화를 즉시지원한다.
  • 사용하는 캐시 솔루션이 스프링 캐시 추상화를 지원하지 않는다면, 직접 캐시 솔루션이 제공하는 API를 사용하거나, 스프링이 제공하는 캐시 추상화를 캐시 솔루션에 맞추는 어댑터를 만들어야 한다.
  • CacheManager와 Cache 인터페이스는 스프링 캐시 추상화에서 중심적인 역할을 한다.
  • CacheManager 인스턴스는 하부 캐시 솔루션이 제공하는 캐시 매니저를 감싸는 래퍼 역할을 하며, Cache 인스턴스의 컬렉션을 관리한다.
  • EhCacheManager는 이캐시Ehcache의 net.sf.ehcache.CacheManager를 감싸고, JCacheCacheManager는 JSR 107 프로바이더의 javax.cache.CacheManager를 감싼다.
  • Cache 인스턴스는 하부 캐시를 감싸며 하부 캐시와 상호작용하는 메서드를 제공한다.
  • EhcacheCache는 net.sf.ehcache.Ehcache의 래퍼고 JCacheCache는 JSR 107 프로바이더의 javax.cache.Cache 인스턴스의 래퍼이다.
  • 스프링은 java.util.concurrent.ConcurrentMap을 하부 캐시로 사용하는 ConcurrentMapCacheManager로 제공한다.
  • ConcurrentMapCacheManager가 관리하는 Cache 인스턴스는 ConcurrentMapCache다.
  • 캐시 솔루션을 위한 CacheManager와 Cache만 직접 구현하면 스프링 캐시에서 해당 솔루션을 사용할 수 있다.
            관리한다.                   <<인터페이스>>
          --------------------->        Cache
         |                                   ^
<<인터페이스>>                              |    
   CacheManager                     ---------------------------------
         ^                       |                  |             |
         |                  EhcacheCache    JcacheCache     ConcurrentMapCache
         |
         |
   ---------------------------------------------------------------------------
   |                      |                       |                          |
EhCacheManager     JCacheCacheManager      SimpleCacheManager       ConCurrentMapCacheManager
  • CacheManager가 Cache 인스턴스를 관리한다.
  • CacheManager 구현은 하부 캐시 솔루션의 캐시 매니저를 둘러싼 래퍼고, Cache 구현은 하부 캐시와 상호작용할 수 있는 연산을 제공한다.
  • EhCacheManager는 EhcacheCache 인스턴스를 관리한다.(하부 캐시 저장소는 이캐시)
  • JCacheCacheManager는 JcacheCache 인스턴스를 관리한다. (하부 캐시 저장소는 JSR 107을 구현하는 캐시 솔루션)
  • ConCurrentMapCacheManager는 ConcurrentMapCache 인스턴스를 관리한다. (하부 캐시 저장소는 java.util.concurrent.ConcurrentMap)
  • CacheManager 인터페이스를 구현하는 SimpleCacheManager 클래스로 SimpleCacheManager는 테스트하거나 간단한 캐싱 시나리오를 처리할 때 유용하다.
  • java.util.concurrent.ConcurrentMap을 하부 캐시 저장소로 사용하려면, ConcurrentMapCacheManager 대신 SimpleCacheManager를 사용해 캐시를 관리할 수 있다.

CacheManager 설정

<bean id="myCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean
                    class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                    <property name="name" value="fixedDepositList" />
                </bean>
                <bean
                    class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                    <property name="name" value="fixedDeposit" />
                </bean>
            </set>
        </property>
    </bean>
  • SimpleCacheManager의 caches 프로퍼티는 SimpleCacheManager 인스턴스가 관리할 캐시 컬렉션을 지정한다.
  • ConcurrentMapCacheFactoryBean을 사용해 ConcurrentMapCache 인스턴스(java.util.concurrent.ConcurrentHashMap 인스턴스를 사용하는 Cache 인스턴스)를 하부 캐시 저장소로 쉽게 설정할 수 있다.
  • ConcurrentMapCacheFactoryBean의 name 프로퍼티는 캐시의 이름을 설정한다.
  • SimpleCacheManager 인스턴스가 fixedDepositList와 fixedDeposit 캐시를 관리한다.
  • 애플리케이션에 적합한 CacheManager를 설정한 후에 스프링 캐시 추상화를 어떻게 사용할지 결정해야 한다.
  • 스프링 캐시 수상화를 캐시 애너테이션(@Cacheable, @CacheEvict, @CachePut 등)을 통해 사용할 수 있고, 스프링의 cache 스키마를 통해 사용할 수 있다.

캐시 애너테이션

  • 스프링 cache 스키마의 <annotation-driven> 엘리먼트를 설정한다.
    <cache:annotation-driven cache-manager="myCacheManager"/>
  • cache-manager 속성은 캐시를 관리하는 CacheManager 빈을 가리킨다.
  • CacheManager 빈 이름이 cacheManager라면 cache-manager 속성을 지정하지 않아도 된다.
  • 자바 기반 설정시에 @EnableCaching 사용해 캐시 애너테이션을 활성화 한다.

@Cacheable

  • 메서드에 @Cacheable 설정하면 메서드의 반환값을 캐시에 넣는다
  • @Cacheable의 key 속성은 반환한 값을 캐시에 저장할 때 사용할 키를 지정한다.
  • key 속성을 지정하지 않으면 디폴트로 스프링 SimpleKeyGenerator 클래스를 사용해 메서드가 반환한 값을 캐시에 저장할 때 사용할 키를 생성한다.
  • SimpleKeyGenerator는 메서드 시그니처와 인수를 사용해 키를 계산한다.
  • <annotation-driven>의 key-generator 속성을 사용하면 디폴트 키 생성기를 커스텀 key Generator 구현으로 바꿀 수 있다.
    @Cacheable(cacheNames = { "fixedDepositList" })
    public List<FixedDepositDetails> findFixedDepositsByBankAccount(int bankAccountId) {
        logger.info("findFixedDepositsByBankAccount method invoked");
        return myFixedDepositDao.findFixedDepositsByBankAccount(bankAccountId);
    }
  • @Cacheable 애너테이션의 cacheNames 속성은 반환값을 캐시할 캐시 영역을 지정한다.
  • MyBank 애플리케이션을 위해 fixedDepositList 캐시 영역을 만든다.
  • @Cacheable은 findFixedDepositsByBankAccount 메서드가 반환하는 값을 fixedDepositList 캐시에 저장한다.
  • key 속성을 지정하지 않아서 SimpleKeyGenerator는 메서드 인수인 bankAccountId 값을 키로 사용한다.
  • @Cacheable을 설정한 메서드가 호출되면 설정된 KeyGenerator가 키를 계산한다.
  • 키가 캐시에 이미 들어 있으면 @Cacheable 메서드가 호출되지 않는다.
  • 키가 캐시에 들어 있지 않으면, @Cacheable 메서드가 호출되고 반환값을 계산한 키를 사용해 캐시에 넣느낟.
  • SimpleKeyGenerator를 키 생성기로 사용하는 경움, 메서드에 전달한 인수가 모두 같을 때 @Cacheable을 설정한 메서드를 호출하지 않는다.
  • 하지만 인수 중 단 하나라도 다른 값을 사용하면 @Cacheable을 설정한 메서드가 호출된다.

@CacheEvict

  • 어떤 메서드가 호출되면 캐시에 잇는 데이터를 모두 비울 때 @CacheEvict를 설정한다.
  • 새로운 정기 예금이 생성되면 FixedDepositServiceImpl의 findFixedDepositByBankAccount 메서드에 의해 저장된 정기 예금 정보를 반드시 캐시에서 제거해야 한다.
  • 그래야 다음에 findFixedDepositByBankAccount 메서드가 호출될 때 새로 만든 정기 예금 정보를 데이터베이스에 가져올 수 있다.
    @Override
    @Transactional(transactionManager = "jmsTxManager")
    @CacheEvict(cacheNames = { "fixedDepositList" }, allEntries = true, beforeInvocation = true)
    public void createFixedDeposit(final FixedDepositDetails fdd) throws Exception {
        logger.info("createFixedDeposit method invoked");
        jmsTemplate.send("emailQueueDestination", new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                TextMessage textMessage = session.createTextMessage();
                textMessage.setText(fdd.getEmail());
                return textMessage;
            }
        });

        // --this JMS message goes to the default destination configured for the
        // JmsTemplate
        jmsTemplate.send(new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                ObjectMessage objectMessage = session.createObjectMessage();
                objectMessage.setObject(fdd);
                return objectMessage;
            }
        });

        jmsTemplate.convertAndSend("aDestination", "Hello world", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws JMSException {
                message.setBooleanProperty("printOnConsole", true);
                return message;
            }
        });

    }
  • createFixedDeposit 메서드에서 @CacheEvict는 스프링이 fixedDepositList 캐시 영역에서 모든 캐시 엔트리를 지우도록 지시한다.
  • cacheNames 속서은 캐시 원소를 없앨 캐시영역을 지정하며, allEntries 속성은 지정한 캐시 영역에서 모든 엔트리를 비울지, 일부만 비울지 결정한다.
  • 특정 캐시 아이템을 캐시에 비운다면 key 속성을 사용해 아이템을 저장할 때 사용한 키를 지정한다.
  • 또한 condition 속서을 사용하면 조건에 따라 아이템을 비울 수도 있다.
  • condition과 key 속성은 SpEL을 사용해 값을 지정하도록 허용한다. 복잡한 조건을 적용해서 캐시를 비울 수 있다.
  • beforeInvocation은 캐시를 메서드 실행 이전이나 이후 중 언제 비울지 결정한다.
  • beforeInvocation 속성의 값이 true이므로 createFixedDeposit 메서드를 호출하기 전에 캐시를 비운다.

@CachePut

  • 스프링은 추가로 항상 메서드를 호출해서 반환값을 캐시에 넣어야 한다는 의미로 @CachePut 애너테이션을 지원한다.
  • @CachePut은 스프링에게 캐시에 키가 이미 있는 경우 메서드를 호출하지 말라고 지시하는 @Cacheable과 다르다.

    @Override
    @CachePut(cacheNames = { "fixedDeposit" }, key = "#fixedDepositId")
    public FixedDepositDetails getFixedDeposit(int fixedDepositId) {
        logger.info("getFixedDeposit method invoked with fixedDepositId " + fixedDepositId);
        return myFixedDepositDao.getFixedDeposit(fixedDepositId);
    }

    @Override
    @Cacheable(cacheNames = { "fixedDeposit" }, key = "#fixedDepositId")
    public FixedDepositDetails getFixedDepositFromCache(int fixedDepositId) {
        logger.info("getFixedDepositFromCache method invoked with fixedDepositId " + fixedDepositId);
        throw new RuntimeException(
                "This method throws exception because FixedDepositDetails object must come from the cache");
    }
  • @CachePut은 getFixedDeposit 메서드에 설정한다.
  • getFixedDeposit 메서드가 항상 호출되고, 메서드가 반환하는 FixedDepositDetails 객체가 fixedDeposit 캐시에 저장된다.
  • cacheNames은 반환된 FixedDepositDetails를 저장할 때 캐시 이름을 지정한다.
  • key 속성은 반환된 FixedDepositDetails 객체를 저장할 때 사용할 키를 지정한다.
  • key 속성은 키를 지정하기 위해 SpEL을 사용한다.
  • key 속성값인 #fixedDepositId는 getFixedDeposit 메서드에 전달된 fixedDepositId 인수를 가리킨다.
  • getFixedDeposit 메서드가 반환하는 FixedDepositDetails 객체를 fixedDeposit 캐시에 저장할 때 메서드 인수인 fixedDepositId 값을 키로 쓴다.
  • getFixedDepositFromCache 메서드는 @Cacheable을 지정한 key 속성값에 따라 캐시에 FixedDepositDetails 객체를 읽는다.
  • getFixedDepositFromCache 메서드는는 단시 RuntimeException을 던지기만 한다.
  • key 속성은 getFixedDepositFromCache 메서드에 전달된 fixedDepositId 인수를 가리킨다.
  • FixedDepositDetails 객체를 캐시에서 찾지 못하면 getFixedDepositFromCache가 호출되고, 이때 RuntimeException이 발생한다.

스프링 cache 스키마를 사용해 캐시 설정

  • 애너테이션 대신 cache 스키마를 사용해 애플리케이션 캐시를 설정할 수 있따.
    <cache:advice id="cacheAdvice" cache-manager="myCacheManager">
        <cache:caching cache="fixedDepositList">
            <cache:cache-evict method="createFixedDeposit"
                all-entries="true" before-invocation="true" />
            <cache:cacheable method="findFixedDepositsByBankAccount" />
        </cache:caching>
        <cache:caching cache="fixedDeposit">
            <cache:cache-put method="getFixedDeposit" key="#fixedDepositId" />
            <cache:cacheable method="getFixedDepositFromCache"
                key="#fixedDepositId" />
        </cache:caching>
    </cache:advice>

    <aop:config>
        <aop:advisor advice-ref="cacheAdvice"
            pointcut="execution(* sample.spring.chapter10.bankapp.service.FixedDepositService.*(..))" />
    </aop:config>
  • <advice>의 cache-manager 속성은 캐시를 관리할 때 사용할 CacheManager 빈을 지정한다.
  • <caching>의 cache 속성이 지정한 캐시 영역의 캐시 동작을 설명한다.
  • <cache-evict>, <cache-put>, <cacheable> 순서대로 @CacheEvict, @CachePup, @Cacheable에 해당한다.
  • aop 스키마의 config 엘리먼트를 사용해 정의한 캐시 동작을 FixedDepositService 인터페이스가 정의한 메서드에 적용한다.
  • <advisor>의 advice-ref 속성은 캐시 동작을 정의하는 <advisor>를 참조하고 pointcut 속성은 캐시 동작을 적용할 메시지를 지정한다.

 

배워서 바로 쓰는 스프링프레임워크
애시시 사린, 제이 샤르마 지음
오현석 옮김

반응형

'JAVA > Spring' 카테고리의 다른 글

검증과 데이터바인딩  (0) 2022.01.28
AOP  (0) 2022.01.28
데이터베이스 연결  (0) 2022.01.05
빈과 빈 정의 커스텀  (0) 2021.12.26
의존관계주입  (0) 2021.12.26