EhCacheアノテーションを使用したSpringでのキャッシング

公開: 2022-03-11

EhCacheは広く使用されている純粋なJavaキャッシュであり、SpringやHibernateなどの最も一般的なJavaフレームワークと簡単に統合できます。 プロジェクトに簡単に統合できるため、Javaアプリケーションにとって最も便利な選択肢と見なされることがよくあります。 特に:

  • プロジェクトにJARを含めるだけで設定できます。 追加のインストール手順は必要ありません。
  • アプリケーションと同じプロセスで実行されるため、高速です。 実行するために追加のサービスは必要ありません。

つまり、EhCacheは純粋なJavaアプリケーションに最適です。

さらに、EhCache Spring Annotationsを使用すると、メソッドの実装を変更することなく、キャッシュ可能なメソッドにアノテーションを追加するだけで、Springアプリケーションにシームレスに統合できます。

EhCacheは、Springプロジェクト向けの優れたキャッシングソリューションです。

EhCacheは、プログラムでキャッシュを操作するための簡単で豊富なAPIを提供しますが、この記事では主に、EhCacheSpringAnnotationsを使用してSpringアプリケーションを邪魔にならない方法でブーストすることに焦点を当てています。 Spring MVCプロジェクトをセットアップし、TomcatにRESTfulWebサービスをデプロイします。 次に、EhCacheがWebサービスに統合されます。

プロジェクトの概要

サンプルプロジェクトのコンテキストでEhCacheアノテーションを示します。 Tomcat8サーバーでホストされるSpringMVCベースのWebサービスをセットアップします。

私はEclipseでプロジェクトを開発しました。これは、ここの手順に従ってインストールできます。

Tomcatの最新の安定バージョンであるTomcat8は、ここからダウンロードできます。

もちろん、これらの特定のプラットフォームはEhCacheの要件ではありません。 お気に入りのIDEとサーバーをいつでも選択できます。

EhCache SpringAnnotationsJARはこちらから入手できます。 ご覧のとおり、バージョンごとに2つのJARがあります。1つは依存関係あり、もう1つは依存関係なしです。 依存関係のあるものには、EhCacheアノテーションが機能するために必要なEhCache2とSpring3も含まれています。 依存関係のあるものをダウンロードしてビルドパスに追加すると、セットアップが簡単になります。

EhCache SpringAnnotationsはSpring4とも互換性がありますが、個別に構成する必要があります。 プロジェクトが近い将来EhCache3をサポートするかどうかは明らかではありません。 EhCache 3を使用している、または使用する予定のある人には、この記事で説明する注釈アプローチはお勧めしません。

最後に、Mavenを使用してすべてを管理します。 Mavenには、ほとんどのEclipseインストールがあらかじめパッケージ化されていますが、ここから入手することもできます。 この記事の後半で示すように、SpringMVCとEhCacheSpringAnnotationsの依存関係はかなり簡単に追加できます。

プロジェクトの設定

これまでにSpringプロジェクトを設定したことがない場合は、このテーマに関するStefanVargaの投稿も参考になるかもしれません。

このデモンストレーションでは、Mavenアーキタイプmaven-archetype-webappを使用して基本的なプロジェクトをセットアップします。 全体的なファイル構造は次のようになります。

初期のSpringプロジェクト構造。

com.toptal.blogcom.toptal.blog.cache 、およびcom.toptal.blog.serviceの3つのパッケージを使用して、ディレクトリ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" />を追加して、Beanを自動的にスキャンして登録するSpringの基本パッケージを定義しましょう。 springrest-servlet.xml構成は次のようになります。

 <beans ... > <mvc:annotation-driven /> <context:component-scan base-package="com.toptal.blog" /> </beans>

シンプルなRESTfulWebサービス

プロジェクトが適切に構成されたので、簡単な「メッセージサービス」APIを実装しましょう。 基本パッケージproject.toptal.blogに、 SpringRestControllerWithEhCache.javaを追加します。1つのGETメソッドでIDでメッセージを取得し、1つのPOSTメソッドでIDでメッセージを設定します。

 @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; } }

MessageServiceクラスをcom.toptal.blog.serviceで定義します。 システムオブレコード(SOR)に保存されているメッセージにアクセスします。 本番アプリでは、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にあるHTTPGETリクエストで「test_message」を取り戻すことができるはずです。 テストを行うための便利なRESTクライアントとしてInsomniaを使用しました。

EhCacheSpringアノテーションをプラグインする

それでは、EhCacheを機能させましょう。 EhCacheを適切に実行するようにプロジェクトを構成するには、いくつかの簡単な手順を実行するだけです。

EhCache Spring Annotationsを使用すると、EhCacheをアプリに簡単かつシームレスにデプロイできます。

ステップ1:EhCacheSpringアノテーションを使用するように依存関係を更新する

Mavenのpom.xmlにEhCacheSpringAnnotations依存関係を追加します。

 <!-- 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を追加するだけです。 たとえば、 MessageServicegetMessageメソッドに@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でHTTPPOSTリクエストを発行してID=1のメッセージを作成し、ID=のメッセージを取得します。 1回、 http://localhost:8080/EhCacheExample/message/1へのGETリクエストを使用。 以下のコンソール出力でわかるように、WebサービスはSORに、最初にメッセージを要求したときにメッセージを取得するように要求しますが、次の2つの要求では取得せず、代わりにキャッシュされたメッセージを返します。 timeToLiveSecondsを10と定義したため、Webサービスは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には2つの引数があるため、 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 MVCRESTfulWebアプリケーションを作成しました。 次に、既存のアプリケーションコードを1行も変更せずに、EhCacheSpringAnnotationsを使用してEhCacheをアプリケーションにシームレスに統合しました。 EhCache Spring Annotationsは、インストールが簡単で(Maven依存関係を追加することにより)、使いやすく(メソッドにアノテーションを追加することにより)エレガントであることを示しました。

参考文献

EhCacheのドキュメントはここにあり、EhCacheSpringAnnotationsのドキュメントはここにあります。

また、GitHubでこの記事で説明されているサンプルプロジェクトを確認してください。