EhCache 주석을 사용하여 Spring에서 캐싱
게시 됨: 2022-03-11EhCache는 Spring 및 Hibernate와 같은 가장 널리 사용되는 Java 프레임워크와 쉽게 통합될 수 있는 널리 사용되는 순수 Java 캐시입니다. 프로젝트에 쉽게 통합될 수 있기 때문에 종종 Java 애플리케이션에 가장 편리한 선택으로 간주됩니다. 특히:
- 프로젝트에 JAR을 포함하기만 하면 설정할 수 있습니다. 추가 설치 단계가 필요하지 않습니다.
- 응용 프로그램과 동일한 프로세스로 실행되므로 빠릅니다. 실행하는 데 추가 서비스가 필요하지 않습니다.
요컨대, EhCache는 모든 순수 Java 애플리케이션에 탁월한 선택입니다.
또한 EhCache Spring Annotations를 사용하면 메서드 구현을 수정하지 않고 캐시 가능한 메서드에 주석을 추가하기만 하면 모든 Spring 애플리케이션에 원활하게 통합할 수 있습니다.
EhCache는 프로그래밍 방식으로 캐시를 조작하기 위한 간단하고 풍부한 API를 제공하지만 이 기사에서는 주로 EhCache Spring Annotations를 사용하여 덜 방해가 되는 방식으로 Spring 애플리케이션을 향상하는 데 중점을 둡니다. Spring MVC 프로젝트를 설정하고 Tomcat에 RESTful 웹 서비스를 배포합니다. 그러면 EhCache가 웹 서비스에 통합됩니다.
프로젝트 개요
예제 프로젝트의 맥락에서 EhCache 주석을 시연할 것입니다. Tomcat 8 서버에서 호스팅되는 Spring MVC 기반 웹 서비스를 설정합니다.
Eclipse에서 프로젝트를 개발했으며 여기의 지침에 따라 설치할 수 있습니다.
안정적인 최신 버전의 Tomcat인 Tomcat 8은 여기에서 다운로드할 수 있습니다.
물론 이러한 특정 플랫폼은 EhCache에 대한 요구 사항이 아닙니다. 항상 좋아하는 IDE와 서버를 선택할 수 있습니다.
EhCache Spring Annotations JAR은 여기에서 사용할 수 있습니다. 보시다시피, 각 버전에는 두 개의 JAR이 있습니다. 하나는 종속성이 있고 다른 하나는 종속성이 없습니다. 종속성이 있는 항목에는 EhCache 주석이 작동하는 데 필요한 EhCache 2 및 Spring 3도 포함됩니다. 종속성이 있는 항목을 다운로드하여 빌드 경로에 추가하면 설정이 더 쉽습니다.
EhCache Spring Annotations는 Spring 4와도 호환되지만 별도로 구성해야 합니다. 프로젝트가 가까운 장래에 EhCache 3를 지원할지는 확실하지 않습니다. EhCache 3를 사용 중이거나 사용하려는 사람들에게는 이 기사에서 논의된 주석 접근 방식을 권장하지 않습니다.
마지막으로 Maven을 사용하여 모든 것을 관리합니다. Maven은 대부분의 Eclipse 설치와 함께 사전 패키징되어 제공되지만 여기에서도 얻을 수 있습니다. Spring MVC 및 EhCache Spring Annotations 종속성은 이 기사의 뒷부분에서 볼 수 있듯이 상당히 쉽게 추가할 수 있습니다.
프로젝트 설정
이전에 Spring 프로젝트를 설정한 적이 없다면 주제에 대한 Stefan Varga의 게시물에서 정보를 얻을 수도 있습니다.
이 데모에서는 Maven 원형 maven-archetype-webapp
을 사용하여 기본 프로젝트를 설정합니다. 전체 파일 구조는 다음과 같습니다.
com.toptal.blog
, com.toptal.blog.cache
및 com.toptal.blog.service
의 세 가지 패키지가 있는 src/main/java
디렉토리를 만듭니다. 우리의 응용 프로그램 소스는 아래에서 자세히 설명하는 것처럼 이러한 패키지에 포함됩니다.
web.xml
에 "springrest"라는 Tomcat 서블릿을 정의해 보겠습니다.
<web-app> ... <servlet> <servlet-name>springrest</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springrest</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
명시적으로 달리 지정하지 않는 한 Spring MVC DispatcherServlet
은 WEB-INF
디렉토리에서 {servlet-name}-servlet.xml
이라는 XML 구성 파일을 찾습니다. springrest-servlet.xml
이라는 설정 파일을 만들어 봅시다. @RequestMapping
주석이 달린 Spring 프로세스 컨트롤러 메서드를 활성화하려면 이 파일에 <mvc:annotation-driven />
을 추가하기만 하면 됩니다. 또한 <context:component-scan base-package="com.toptal.blog" />
를 추가하여 Spring이 빈을 자동으로 스캔하고 등록할 수 있도록 기본 패키지를 정의해보자. springrest-servlet.xml
구성은 다음과 같습니다.
<beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>
간단한 RESTful 웹 서비스
이제 프로젝트가 제대로 구성되었으므로 간단한 "메시지 서비스" API를 구현해 보겠습니다. 기본 패키지 project.toptal.blog
에서 SpringRestControllerWithEhCache.java
를 추가하고 ID별로 메시지를 가져오는 GET 메서드와 ID로 메시지를 설정하는 POST 메서드를 추가합니다.
@RestController @RequestMapping( "/" ) public class SpringRestControllerWithEhCache { @Autowired MessageService messageService; @RequestMapping( value = "/message/{id}", method = RequestMethod.GET ) public String getMessage( @PathVariable Integer id ) { String message = messageService.getMessage( id ); System.out.println( "get message ["+message+"] at "+new Date() ); return message; } @RequestMapping( value = "/message/set/{id}/{message}", method = RequestMethod.POST ) public String setMessage( @PathVariable Integer id, @PathVariable String message ) { System.out.println( "set message ["+message+"] at "+new Date() ); messageService.setMessage( id, message ); return message; } }
우리는 com.toptal.blog.service
에서 MessageService
클래스를 정의할 것입니다. SOR(System of Records)에 저장된 메시지에 액세스합니다. 프로덕션 앱에서 SOR은 관계형 데이터베이스와 비슷합니다. 단순화를 위해 HashMap
을 사용합니다.
@Service public class MessageService { private ConcurrentHashMap<Integer, String> messages = new ConcurrentHashMap<Integer, String>(); public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } public void setMessage( Integer id, String message ){ messages.put( id, message ); } }
이제 프로젝트를 WAR로 내보내고 Tomcat에 배포하면 http://localhost:8080/EhCacheExample/message/set/1/test_message
에서 HTTP POST 요청을 만들어 ID=1에 대해 "test_message"와 같은 메시지를 설정할 수 있습니다. http://localhost:8080/EhCacheExample/message/set/1/test_message
. 그런 다음 http://localhost:8080/EhCacheExample/message/1
에서 HTTP GET 요청으로 "test_message"를 다시 가져올 수 있어야 합니다. 나는 내 테스트를 수행하기 위해 편리한 REST 클라이언트로 Insomnia를 사용했습니다.
플러그인 EhCache 스프링 주석
이제 EhCache가 작동하도록 합시다. EhCache를 올바르게 실행하도록 프로젝트를 구성하는 데 몇 가지 빠른 단계만 거치면 됩니다.
1단계: EhCache Spring 주석을 사용하도록 종속성 업데이트
Maven의 pom.xml
에 EhCache Spring Annotations 종속성을 추가합니다.
<!-- ehcache --> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency>
2단계: 사용자 정의 캐시 관리자 설정
Spring에는 내장된 EhCache 캐시 관리자인 org.springframework.cache.ehcache.EhCacheManagerFactoryBean
이 있습니다. 이것은 대부분의 캐싱 상황에 적합하지만 사용자 지정 캐시 관리자를 정의하면 동일한 캐시 관리자를 사용하여 프로그래밍 방식으로 또는 주석으로 캐시를 제어할 수 있기 때문에 유용하다는 것을 알았습니다. 이 기사는 주석에 중점을 두고 있지만 필요할 경우에 대비할 수 있도록 사용자 정의 캐시 관리자를 정의해 보겠습니다. 기본 캐시 관리자를 사용하려면 이 단계를 건너뛸 수 있습니다.

com.toptal.blog.cache.CustomCacheManager
에서 새 클래스를 정의합니다.
public class CustomCacheManager extends net.sf.ehcache.CacheManager{ public CustomCacheManager(){ super(); } /* Add your own cache methods here. * * public void myCustomCacheMethod(){ * // your code here * } * */ }
다음과 같이 springrest-servlet.xml
을 업데이트하여 활성화합니다.
... <ehcache:annotation-driven cache-manager="customCacheManager" /> <bean class="com.toptal.blog.cache.CustomCacheManager" scope="singleton"></bean> ...
3단계: EhCache 구성
마지막으로 클래스 경로에 EhCache 구성 파일 ehcache.xml
을 생성합니다. 기본적으로 Eclipse는 클래스 경로에 src/main/resources
를 포함하며 여기에 파일을 배치합니다. 이 파일은 EhCache가 제대로 작동하는 데 필요합니다. timeToLiveSeconds
와 같은 캐시 이름과 각 캐시의 일부 속성을 정의합니다.
<ehcache xmlms:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <diskStore path="cache" /> <cache name="messageCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="10" overflowToDisk="false" memoryStoreEvictionPolicy="LFU" /> </ehcache>
4단계: 캐시 테스트
이제 모든 설정과 준비가 완료되었으므로 EhCache를 사용하는 것은 쉽고 즐거운 작업이 될 것입니다. 캐시하려는 메서드나 클래스에 @Cacheable
을 추가하기만 하면 됩니다. 예를 들어 MessageService
의 getMessage
메서드에 @Cacheable
을 추가했습니다. 정말 쉽습니다!
@Cacheable( cacheName = "messageCache" ) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); }
캐시가 작동하는지 테스트하기 위해 http://localhost:8080/EhCacheExample/message/set/1/newMessage
에서 HTTP POST 요청을 발행하여 ID=1에 대한 메시지를 생성한 다음 ID=에 대한 메시지를 얻을 수 있습니다. 1 여러 번, GET 요청은 http://localhost:8080/EhCacheExample/message/1
입니다. 아래 콘솔 출력에서 볼 수 있듯이 웹 서비스는 메시지를 처음 요청할 때 SOR에 메시지를 요청하지만 다음 두 요청에 대해서는 요청하지 않고 대신 캐시된 메시지를 반환합니다. timeToLiveSeconds
를 10으로 정의했기 때문에 웹 서비스는 SOR을 호출하여 10초 후에 메시지를 다시 가져옵니다.
set message [newMessage] at Sun Dec 06 23:55:39 MST 2015 get message [newMessage] at Sun Dec 06 23:55:42 MST 2015 Getting data from SOR...... get message [newMessage] at Sun Dec 06 23:55:47 MST 2015 get message [newMessage] at Sun Dec 06 23:55:49 MST 2015 get message [newMessage] at Sun Dec 06 23:55:54 MST 2015 Getting data from SOR......
캐시 새로 고침
이제 우리는 캐시가 제공하는 속도와 편리함을 즐기고 있으며 EhCache는 10초마다 자체적으로 새로 고칠 수 있을 정도로 훌륭합니다. 그러나 SOR이 업데이트된 직후에 새로 고치고 싶다면 어떻게 해야 할까요? EhCache Spring Annotation은 @TriggersRemove
를 제공하여 주석이 달린 메서드가 호출될 때 캐시에서 지정된 키를 제거합니다. 메시지 서비스 API에서는 setMessage
가 호출될 때 캐시된 메시지를 캐시에서 제거해야 합니다. 따라서 다음에 getMessage
요청이 들어올 때 캐시는 SOR에서 새 레코드를 가져옵니다.
@Cacheable( cacheName = "messageCache", keyGenerator = @KeyGenerator ( // method name is not included in cache key to work with @TriggersRemove name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ))) public String getMessage( Integer id ) { System.out.println( "Getting data from SOR......" ); return messages.get( id ); } @TriggersRemove( cacheName = "messageCache", keyGenerator = @KeyGenerator ( name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ))) public void setMessage( @PartialCacheKey Integer id, String message ) { messages.put( id, message ); }
키 생성기는 캐시 관리자가 캐시 키를 생성하는 데 사용합니다. 사전 정의된 캐시 키 생성기 목록은 여기에서 찾을 수 있습니다. 기본적으로 @KeyGenerator
는 메서드 이름과 전달된 매개 변수를 모두 사용하여 캐시 키를 생성합니다. 그러나 setMessage
메소드가 getMessage
와 동일한 키를 생성하고 해당 키와 관련된 캐시된 값을 삭제하기를 원하기 때문에 메시지 ID만 키로 사용하고 키 생성을 위한 메소드 이름을 제거해야 합니다. 따라서 두 메서드 모두에 대해 키 생성기의 includeMethod
속성을 false
로 설정합니다. 또한 setMessage
에는 두 개의 인수가 있으므로 id
매개변수에 EhCache의 @PartialCacheKey
주석을 사용하여 키 생성기가 사용해야 하는 유일한 것임을 지정합니다. 마지막으로 이 리소스 유형에 대해 전용 캐시인 messageCache
를 구성했으므로 키에 대한 ID만 사용하면 다른 리소스 유형과 충돌할 위험이 없습니다.
이제 다음과 같이 ID=1인 메시지에 대해 여러 HTTP 요청을 수행하면 다음과 같습니다.
HTTP POST: http://localhost:8080/EhCacheExample/message/set/1/newMessage1 HTTP GET:http://localhost:8080/EhCacheExample/message/1 HTTP POST: http://localhost:8080/EhCacheExample/message/set/1/newMessage2 HTTP GET:http://localhost:8080/EhCacheExample/message/1
콘솔에 다음이 표시됩니다.
set message [newMessage1] at Tue Dec 08 17:53:44 MST 2015 get message [newMessage1] at Tue Dec 08 17:53:47 MST 2015 Getting data from SOR...... set message [newMessage2] at Tue Dec 08 17:53:50 MST 2015 get message [newMessage2] at Tue Dec 08 17:53:53 MST 2015 Getting data from SOR......
결론
최종 프로젝트 구조는 다음과 같습니다.
이 예제에서는 먼저 간단한 Spring MVC RESTful 웹 애플리케이션을 만들었습니다. 기존 애플리케이션 코드 한 줄도 수정하지 않고 EhCache Spring Annotations를 사용하여 EhCache를 애플리케이션에 원활하게 통합했습니다. 우리는 EhCache Spring Annotations가 설치하기 쉽고(Maven 종속성을 추가하여) 사용하기 편리함(메소드에 주석을 추가함으로써)을 입증했습니다.
추가 읽기
EhCache 문서는 여기에서 찾을 수 있고 EhCache Spring Annotations 문서는 여기에서 찾을 수 있습니다.
또한 GitHub의 이 기사에 설명된 샘플 프로젝트를 확인하십시오.