最近项目中需要用到缓存,在网上搜了大堆资料,发现Ehcache很好用,用法也比较简单。用简单的配置就可以使用了,而且Ehcache可以对页面、对象、数据进行缓存。Spring对Ehcache的支持也非常好。EHCache支持内存和磁盘的缓存,支持多种淘汰算法(LRU、LFU和FIFO)。
一、本教程环境
Spring 3.2.5 支持 http://pan.baidu.com/s/1hq3To8k
Ehcache 对象、数据缓存:http://ehcache.org/downloads/destination?name=ehcache-core-2.5.2-distribution.tar.gz&bucket=tcdistributions&file=ehcache-core-2.5.2-distribution.tar.gz
Ehcache Web页面缓存:http://ehcache.org/downloads/destination?name=ehcache-web-2.0.4-distribution.tar.gz&bucket=tcdistributions&file=ehcache-web-2.0.4-distribution.tar.gz
把jar加入到lib目录下
ehcache-core中可以找到 ehcache.xml 、ehcache.xsd着两个配置文件放到项目src下的配置文件目录
二、页面缓存
页面缓存主要用Filter过滤器对请求的url进行过滤,如果该url在缓存中出现。那么页面数据就从缓存对象中获取,并以gzip压缩后返回。其速度是没有压缩缓存时速度的3-5倍,效率相当之高!
在ehcache.xml中加入如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <!-- name:Cache的唯一标识 maxElementsInMemory:内存中最大缓存对象数 maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大 eternal:Element是否永久有效,一但设置了,timeout将不起作用 overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中 timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大 timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大 意思是从cache中的某个元素从创建到消亡的时间,从创建开始计时,当超过这个时间,这个元素将被从cache中清除 diskPersistent:是否缓存虚拟机重启期数据 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用) --> <pre name="code" class="html"> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="20000" overflowToDisk="true" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" /> <cache name="SimplePageCachingFilter" maxElementsInMemory="20000" maxElementsOnDisk="20000" eternal="false" overflowToDisk="true" timeToIdleSeconds="3600" timeToLiveSeconds="3600" memoryStoreEvictionPolicy="LFU" /></ehcache>
|
拦截代码具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| [java] view plaincopyprint? /** * @Title: PageEhCacheFilter.java * @Package com.cqut.tool.cache * @Description: TODO(用一句话描述该文件做什么) * @author matao@cqrainbowsoft.com * @date 2014-8-1 上午12:29:00 * @version V1.0 */ package com.cqut.tool.cache; 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; /** * 项目名称:<span style="font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;">SpringMVCDemo</span> 类名称:PageEhCacheFilter 类描述: 创建人:熊海 创建时间:2014-8-1 * 上午12:29:00 修改人:熊海 修改时间:2014-8-1 上午12:29:00 修改备注: * * @version 1.0 <span style="font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;">Superc102</span> */ 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 * * <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; } }
|
web.xml加入如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!-- ehcache页面缓存 --> <filter> <filter-name>PageEhCacheFilter</filter-name> <filter-class>com.cqut.tool.cache.PageEhCacheFilter</filter-class> <init-param> <param-name>patterns</param-name> <!-- 配置你需要缓存的url --> <param-value>/module/jsp/news/newsCenter.jsp,indexPicController/getDimensionCode.do</param-value> </init-param> </filter> <filter-mapping> <filter-name>PageEhCacheFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>PageEhCacheFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
|
测试页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <%@ page import="com.cqut.tool.util.PropertiesTool"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+PropertiesTool.getSystemPram("serverName")+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'testcache.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <%=new Date() %> </body> </html>
|
当第一次请求这些页面后,这些页面就会被添加到缓存中,如果缓存没失效的话以后请求这些页面将会从缓存中获取。
把缓存测试页面加入web.xml的配置中后。如果时间是变动的,则表示该页面没有被缓存或是缓存已经过期,否则则是在缓存状态了。
三、对象缓存
对象缓存就是将查询的数据添加到缓存中,下次再次查询相同东西的时候直接从缓存中获取,而不去数据库中查询。
对象缓存一般是针对方法、类而来的。本教程结合Spring的Aop对象、方法缓存来说明。
在application.xml的命名空间中加入下面配置:
xmlns:cache=”http://www.springframework.org/schema/cache“
xmlns:p=”http://www.springframework.org/schema/p“
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
在application.xml的中加入下面配置:
1 2 3 4 5 6 7 8 9
| <!--start spring缓存 --> <cache:annotation-driven /><!-- 支持缓存的配置项<span style="font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;">注解</span> --> <!--start 一般的spring_ehcache缓存管理 --> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/> <!-- EhCache library setup --> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" p:shared="true"/> <!-- 保持使用p:shared="true"同一个缓存 --> <!--end 一般的spring_ehcache缓存管理 --> <!--end spring缓存 -->
|
java测试方法:
1 2 3 4 5 6
| @Override @Cacheable(value="myCache",key="#columnCode+'ColumnsServicegetColumns'",condition="#columnCode<23") public String getColumns(int columnCode){ System.out.println("请求后台:"+columnCode); return "xhay"; }
|
@Cacheable注解可以用在方法或者类级别。当他应用于方法级别的时候,就是如上所说的缓存返回值了。当应用在类级别的时候,这个类的所有方法的返回值都将被缓存。
@Cacheable注解有三个参数,value是必须的,还有key和condition。第一个参数,也就是value指明ehcache.xml配置的缓存名称。
@Cacheable注解的方法的签名来作为key,当然你可以重写key,自定义key可以使用SpEL表达式。
@Cacheable的最后一个参数是condition(可选),同样的,也是引用一个SpEL表达式。但是这个参数将指明方法的返回结果是否被缓存。
在上面这个测试例子中,第一次访问的时候后台会打印日志“请求后台:”,下一次请求的时候直接从缓存中拿数据。不会有后台输出。
想对EhCache做进一步了解的可以参考这一片文章:http://raychase.iteye.com/blog/1545906