Ehcache統合Spring使用ページ、オブジェクトキャッシュ

16609 ワード

http://www.cnblogs.com/hoojo/archive/2012/07/12/2587556.html
一、準備工作
もしあなたのシステムの中でSpring、Hibernateに成功したら。次のEhcacheの準備に入ることができます。
1、jarパッケージをダウンロードする
Ehcacheオブジェクト、データキャッシュ:http://ehcache.org/downloads/destination?name=ehcache-coree-2.5.2-distributions.tar.gz&bucket=tcdistributions&file=ehcache-core-0.25-distributions.gz
Webページキャッシュ:http://ehcache.org/downloads/destination?name=ehcache-web-2.0.4-distributions.tar.gz&bucket=tcdistributions&file=ehcache-web-2.4.2-distributions.gz
2、下記のjarを追加してlibディレクトリの下に包む必要があります。
ehcache-core-2.25.jar
ehcache-web-2.7.4.jarは主にページに対してキャッシュします。
3、現在のプロジェクトのsrcディレクトリに配置ファイルを追加する
ehcache.xml
ehcache.xsd
これらのプロファイルはehcache-coreというjarカバンの中にあります。
二、Ehcacheの基本的な使い方
CacheManager cacheManager = CacheManager.create();
//   
cacheManager = CacheManager.getInstance();
//   
cacheManager = CacheManager.create("/config/ehcache.xml");
//   
cacheManager = CacheManager.create("http://localhost:8080/test/ehcache.xml");
cacheManager = CacheManager.newInstance("/config/ehcache.xml");
// .......
 
//   ehcache        cache
Cache sample = cacheManager.getCache("sample");
//       
BlockingCache cache = new BlockingCache(cacheManager.getEhcache("SimplePageCachingFilter"));
//         
Element element = new Element("key", "val");
sample.put(element);
//         ,     cache          Serializable  
Element result = sample.get("key");
//     
sample.remove("key");
sample.removeAll();
 
//                
for (String cacheName : cacheManager.getCacheNames()) {
    System.out.println(cacheName);
}
//          
for (Object key : cache.getKeys()) {
    System.out.println(key);
}
 
//          
cache.getSize();
//              
cache.getMemoryStoreSize();
//            
cache.getStatistics().getCacheHits();
//            
cache.getStatistics().getCacheMisses();
 
三、ページキャッシュ
ページキャッシュは、主にFilterフィルタで要求されたurlをフィルタリングし、このurlがキャッシュに表示される場合に使用される。ページデータはキャッシュオブジェクトから取得し、gzipで圧縮して返します。そのスピードはキャッシュが圧縮されていない時のスピードの3-5倍で、効率はかなり高いです。ページキャッシュのフィルタにはCachingFilterがあり、一般的にはfilterを拡張したり、カスタムFilterを引き継いだりします。
CachingFilter機能はHTTP応答の内容をキャッシュすることができます。このようにしてキャッシュデータの粒度は比較的粗く、例えばページ全体をキャッシュする。その利点は、使用が簡単で、効率が高く、欠点は十分に柔軟ではなく、再利用の程度が高くないことです。
EHCacheはSimplePageCachingFilter類を使用してFilterキャッシュを実現します。このクラスはCachingFilterから継承され、デフォルトでcache keyを生成するcaculateKey()方法があり、この方法はHTTP要求のURIとクエリ条件を用いてkeyを構成する。Filterを自分で実現し、CachingFilter類を引き継いで、calculateKey()メソッドを上書きして、ユーザー定義のkeyを生成することもできます。
CachingFilterが出力するデータは、ブラウザから送信されたAccept-Ecodingヘッダ情報に基づいてGzip圧縮を行います。
Gzipを使って圧縮する時、二つの問題に注意する必要があります。
1.FilterはGzip圧縮を行う際に、システムのデフォルトコードを採用しています。GBKコードを使用する中国語のページには、オペレーティングシステムの言語をzh_に設定する必要があります。CN.GBKは文字化けの問題があります。
2.デフォルトでは、CachingFilterは、ブラウザから送信された要求ヘッダに含まれるAccept-Enccodingパラメータ値に基づいてGzip圧縮を行うかどうかを判断します。IE 6/7ブラウザはGzip圧縮に対応していますが、要求を送信する時はこのパラメータを持っていません。IE 6/7に対してもGzip圧縮を行うために、Caching Filterを継承することにより、自分のFilterを実現し、具体的な実現の中で方法を上書きすることができます。
具体的な実現の参考:
protected boolean acceptsGzipEncoding(HttpServletRequest request) {

boolean ie6 = headerContains(request, "User-Agent", "MSIE 6.0");

boolean ie7 = headerContains(request, "User-Agent", "MSIE 7.0");

return acceptsEncoding(request, "gzip") || ie6 || ie7;

}
ehcache.xmlに下記の構成を追加します。
<?xml version="1.0" encoding="gbk"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
    <diskStore path="java.io.tmpdir"/>
 
    <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/>
    <!-- 
               
        maxElementsInMemory:             
        eternal:           ,   ,        ,      。
        timeToIdleSeconds:         ,            ,
                                  ,               ,
                          0                 。
        timeToLiveSeconds:         ,                     ,
                                   ,     0                。
        overflowToDisk:     ,        。
        memoryStoreEvictionPolicy:           。
    -->
    <cache name="SimplePageCachingFilter" 
        maxElementsInMemory="10000" 
        eternal="false"
        overflowToDisk="false" 
        timeToIdleSeconds="900" 
        timeToLiveSeconds="1800"
        memoryStoreEvictionPolicy="LFU" />
 
</ehcache>
具体的なコード:
package com.hoo.ehcache.filter;
 
import java.util.Enumeration;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.constructs.blocking.LockTimeoutException;
import net.sf.ehcache.constructs.web.AlreadyCommittedException;
import net.sf.ehcache.constructs.web.AlreadyGzippedException;
import net.sf.ehcache.constructs.web.filter.FilterNonReentrantException;
import net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
 
/**
 * <b>function:</b> mobile        
 * @author hoojo
 * @createDate 2012-7-4   09:34:30
 * @file PageEhCacheFilter.java
 * @package com.hoo.ehcache.filter
 * @project Ehcache
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email [email protected]
 * @version 1.0
 */
public class PageEhCacheFilter extends SimplePageCachingFilter {
 
    private final static Logger log = Logger.getLogger(PageEhCacheFilter.class);
    
    private final static String FILTER_URL_PATTERNS = "patterns";
    private static String[] cacheURLs;
    
    private void init() throws CacheException {
        String patterns = filterConfig.getInitParameter(FILTER_URL_PATTERNS);
        cacheURLs = StringUtils.split(patterns, ",");
    }
    
    @Override
    protected void doFilter(final HttpServletRequest request,
            final HttpServletResponse response, final FilterChain chain)
            throws AlreadyGzippedException, AlreadyCommittedException,
            FilterNonReentrantException, LockTimeoutException, Exception {
        if (cacheURLs == null) {
            init();
        }
        
        String url = request.getRequestURI();
        boolean flag = false;
        if (cacheURLs != null && cacheURLs.length > 0) {
            for (String cacheURL : cacheURLs) {
                if (url.contains(cacheURL.trim())) {
                    flag = true;
                    break;
                }
            }
        }
        //           url       ,           
        if (flag) {
            String query = request.getQueryString();
            if (query != null) {
                query = "?" + query;
            }
            log.info("       :" + url + query);
            super.doFilter(request, response, chain);
        } else {
            chain.doFilter(request, response);
        }
    }
    
    @SuppressWarnings("unchecked")
    private boolean headerContains(final HttpServletRequest request, final String header, final String value) {
        logRequestHeaders(request);
        final Enumeration accepted = request.getHeaders(header);
        while (accepted.hasMoreElements()) {
            final String headerValue = (String) accepted.nextElement();
            if (headerValue.indexOf(value) != -1) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * @see net.sf.ehcache.constructs.web.filter.Filter#acceptsGzipEncoding(javax.servlet.http.HttpServletRequest)
     * <b>function:</b>   ie6/7 gzip  
     * @author hoojo
     * @createDate 2012-7-4   11:07:11
     */
    @Override
    protected boolean acceptsGzipEncoding(HttpServletRequest request) {
        boolean ie6 = headerContains(request, "User-Agent", "MSIE 6.0");
        boolean ie7 = headerContains(request, "User-Agent", "MSIE 7.0");
        return acceptsEncoding(request, "gzip") || ie6 || ie7;
    }
}
ここのPageEhCacheFilterはSimplePageCachingFilterを継承しています。普通はSimplePageCachingFilterで十分です。ここでは現在のシステムニーズを満たすためにカバー操作をしました。SimplePageCachingFilterを使うにはweb.xmlにcacheNameを配置する必要があり、cacheNameはデフォルトはSimplePageCachingFilterで、ehcache.xmlにおけるcache配置に対応しています。
web.xmlに下記の構成を追加します。
<!--   、gzip        -->
<filter>
    <filter-name>PageEhCacheFilter</filter-name>
    <filter-class>com.hoo.ehcache.filter.PageEhCacheFilter</filter-class>
    <init-param>
        <param-name>patterns</param-name>
        <!--         url -->
        <param-value>/cache.jsp, product.action, market.action </param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>PageEhCacheFilter</filter-name>
    <url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>PageEhCacheFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
</filter-mapping>
これらのページが最初に要求されると、これらのページはキャッシュに追加され、後でこれらのページがキャッシュから取得されるように要求される。cache.jspページでこのページがキャッシュされているかどうかをテストできます。new Date()%>時間が変動している場合、このページはキャッシュされていない、またはキャッシュされていないということです。
四、オブジェクトキャッシュ
オブジェクトキャッシュとは、クエリーのデータをキャッシュに追加し、次回再検索するときは、データベースで検索せずにキャッシュから直接取得することです。
オブジェクトキャッシュは、一般的に、方法、クラスに対して、SpringのAopオブジェクト、方法キャッシュを組み合わせると簡単です。ここではカットを使ってプログラムします。Springを使ったMethodInterceptorか@Aspectを使います。
コードは以下の通りです
package com.hoo.common.ehcache;
 
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
 
/**
 * <b>function:</b>             
 * @author hoojo
 * @createDate 2012-7-2   06:05:34
 * @file MethodCacheInterceptor.java
 * @package com.hoo.common.ehcache
 * @project Ehcache
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email [email protected]
 * @version 1.0
 */
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {
 
    private static final Logger log = Logger.getLogger(MethodCacheInterceptor.class);
    
    private Cache cache;
 
    public void setCache(Cache cache) {
        this.cache = cache;
    }
 
    public void afterPropertiesSet() throws Exception {
        log.info(cache + " A cache is required. Use setCache(Cache) to provide one.");
    }
 
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        Object[] arguments = invocation.getArguments();
        Object result;
 
        String cacheKey = getCacheKey(targetName, methodName, arguments);
        Element element = null;
        synchronized (this) {
            element = cache.get(cacheKey);
            if (element == null) {
                log.info(cacheKey + "     : " + cache.getName());
                //        
                result = invocation.proceed();
                element = new Element(cacheKey, (Serializable) result);
                cache.put(element);
            } else {
                log.info(cacheKey + "    : " + cache.getName());
            }
        }
        return element.getValue();
    }
 
    /**
     * <b>function:</b>                
     * @author hoojo
     * @createDate 2012-7-2   06:12:39
     * @param targetName    
     * @param methodName     
     * @param arguments   
     * @return       
     */
    private String getCacheKey(String targetName, String methodName, Object[] arguments) {
        StringBuffer sb = new StringBuffer();
        sb.append(targetName).append(".").append(methodName);
        if ((arguments != null) && (arguments.length != 0)) {
            for (int i = 0; i < arguments.length; i++) {
                sb.append(".").append(arguments[i]);
            }
        }
        return sb.toString();
    }
}
ここでの方法ブロックは主にブロックしたい種類の方法をブロックし、方法のクラスパス+方法名+パラメータ値を組み合わせたcache keyがキャッシュcacheに存在するかどうかを判断します。存在する場合は、キャッシュからそのオブジェクトを取り出して、私たちが求めるリターンタイプに変換します。いいえ、この方法で戻ったオブジェクトをキャッシュに追加すればいいです。特に、現在の方法のパラメータと返却値のオブジェクトタイプは、順序付けが必要である。
私たちは、SrcディレクトリにappicationContact.xmlを追加して、MethodCacheInterceptorブロックの構成を完了する必要があります。この構成は、私たちのcacheオブジェクトを注入し、どのcacheがオブジェクトキャッシュを管理し、その後、どのような種類、方法がブロックのスキャンに参加しますか?
追加設定は以下の通りです。
<context:component-scan base-package="com.hoo.common.interceptor"/> 
 
<!--   eh      -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
 
<!--            bean   -->
<bean id="simpleCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager" ref="cacheManager" />
    <!--        ehcache.xml       -->
    <property name="cacheName" value="mobileCache" />
</bean>
 
<!--            ,          -->
<bean id="methodCacheInterceptor" class="com. hoo.common.interceptor.MethodCacheInterceptor">
    <property name="cache" ref="simpleCache"/>
</bean>
 
<!--            (     ,           ) -->
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <!--     aop   -->
    <property name="advice" ref="methodCacheInterceptor" />
    <!--              -->
    <!--  
        .                            
        ###  +                                
        ###  *                                
        ###  \Escape  Regular expression                        
    -->                 
    <!-- .*       (    )   print  -->
    <property name="patterns">
        <list>
            <value>com.hoo.rest.*RestService*\.*get.*</value>
            <value>com.hoo.rest.*RestService*\.*search.*</value>
        </list>
    </property>
</bean>
ehcache.xmlに以下のcacheの構成を追加します。
<cache name="mobileCache"
        maxElementsInMemory="10000"
        eternal="false"
        overflowToDisk="true"
        timeToIdleSeconds="1800"
        timeToLiveSeconds="3600"
        memoryStoreEvictionPolicy="LFU" />