EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider,同时在项目开发中也被广泛使用到。接下来就以用户缓存为例,基于javaweb项目来搭建ehcache缓存系统。
主要涉及核心原理和实现要点:项目是基于spring框架来统一管理和配置bean的,所以在spring中配置缓存,定义EhCacheManagerFactoryBean,同时指向其ehcache配置文件ehcache.xml,ehcache.xml则有我们自己根据项目情况来定义一些缓存策略,如:cache中元素的生存时间、cache 中最多可以存放的元素的数量和内存存储与释放策略等等。java代码中则可以定义一个统一的缓存管理类去持有net.sf.ehcache.CacheManager实例,来代理往数据库中操作数据的行为。关于CacheManager实例的获取,则可以通过实现ApplicationContextAware, DisposableBean接口,分别重写其setApplicationContext()方法,注入ApplicationContext到静态变量中和destroy()方法,在ApplicationContext关闭时清理静态变量。这样以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext,进而调用其getBean()方法来获得CacheManager实例。拿数据是先getByCache(),若缓存中存在数据则直接返回该数据,若缓存中不存在数据,再执行getByDb(),从数据库中拿数据,同时将数据存进缓存中。
1、首先从web.xml部署文件入口进行配置
1 <!-- 应用程序Spring上下文配置 --> 2 <context-param> 3 <param-name>contextConfigLocation</param-name> 4 <param-value> 5 classpath*:applicationContext*.xml, 6 </param-value> 7 </context-param> 8 <!-- 自定义监听器 继承自spring上下文加载监听器 --> 9 <listener> 10 <listener-class>com.schoolnet.sys.listener.WebContextListener</listener-class> 11 </listener>
继承自spring上下文加载监听器的WebContextListener类,可以在web启动时做一些处理
WebContextListener .java
1 public class WebContextListener extends org.springframework.web.context.ContextLoaderListener { 2 3 @Override 4 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 5 if (!printKeyLoadMessage()){ 6 return null; 7 } 8 return super.initWebApplicationContext(servletContext); 9 } 10 private static boolean printKeyLoadMessage(){ 11 StringBuilder sb = new StringBuilder(); 12 sb.append("\r\n======================================================================\r\n"); 13 sb.append("\r\n 欢迎使用 校园网 - EhCache缓存系统搭建\r\n"); 14 sb.append("\r\n======================================================================\r\n"); 15 System.out.println(sb.toString()); 16 return true; 17 } 18 }
2、applicationContex.xml中配置ehcache缓存
1 <!-- 加载配置属性文件 --> 2 <bean id="propertyConfigurer" 3 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 4 5 <property name="fileEncoding" value="UTF-8" /> 6 7 <property name="locations"> 8 <list> 9 <value>classpath:schoolnet.properties</value> 10 </list> 11 </property> 12 </bean> 13 <!-- 缓存配置 --> 14 <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> 15 <property name="configLocation" value="classpath:${ehcache.configFile}" /> 16 </bean>
3、schoolnet.properties
ehcache.configFile=cache/ehcache.xml(源码包下的cache文件夹中)
4、ehcache.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ehcache updateCheck="false" name="defaultCache"> 3 4 <diskStore path="../temp/jeesite/ehcache" /> 5 6 <!-- 默认缓存配置. --> 7 <defaultCache maxEntriesLocalHeap="100" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" 8 overflowToDisk="true" maxEntriesLocalDisk="100000" /> 9 10 <!-- 系统缓存 --> 11 <cache name="sysCache" maxEntriesLocalHeap="100" eternal="true" overflowToDisk="true"/> 12 13 <!-- 用户缓存 --> 14 <cache name="userCache" maxEntriesLocalHeap="100" eternal="true" overflowToDisk="true"/> 15 16 <!-- 系统活动会话缓存 --> 17 <cache name="activeSessionsCache" maxEntriesLocalHeap="10000" overflowToDisk="true" 18 eternal="true" timeToLiveSeconds="0" timeToIdleSeconds="0" 19 diskPersistent="true" diskExpiryThreadIntervalSeconds="600"/> 20 21 <!-- 简单页面缓存 22 <cache name="SimplePageCachingFilter" maxEntriesLocalHeap="100" eternal="false" overflowToDisk="true" 23 timeToIdleSeconds="120" timeToLiveSeconds="120" memoryStoreEvictionPolicy="LFU"/> --> 24 25 </ehcache>
一些配置参数的说明
maxElementsInMemory :cache 中最多可以存放的元素的数量。如果放入cache中的元素超过这个数值,有两种情况:1、若overflowToDisk的属性值为true,会将cache中多出的元素放入磁盘文件中。2、若overflowToDisk的属性值为false,会根据memoryStoreEvictionPolicy的策略替换cache中原有的元素。
eternal :意思是是否永驻内存。如果值是true,cache中的元素将一直保存在内存中,不会因为时间超时而丢失,所以在这个值为true的时候,timeToIdleSeconds和timeToLiveSeconds两个属性的值就不起作用了。
timeToIdleSeconds :就是访问这个cache中元素的最大间隔时间。如果超过这个时间没有访问这个cache中的某个元素,那么这个元素将被从cache中清除。
timeToLiveSeconds : 这是cache中元素的生存时间。意思是从cache中的某个元素从创建到消亡的时间,从创建开始计时,当超过这个时间,这个元素将被从cache中清除。
overflowToDisk :溢出是否写入磁盘。系统会根据标签<diskStore path=”java.io.tmpdir”/> 中path的值查找对应的属性值,如果系统的java.io.tmpdir的值是 D:/temp,写入磁盘的文件就会放在这个文件夹下。文件的名称是cache的名称,后缀名的data。如:CACHE_FUNC.data。这个属性在解释maxElementsInMemory的时候也已经说过了。
diskExpiryThreadIntervalSeconds :磁盘缓存的清理线程运行间隔
memoryStoreEvictionPolicy :内存存储与释放策略。有三个值:
LRU -least recently used
LFU -least frequently used
FIFO-first in first out, the oldest element by creation time
diskPersistent : 是否持久化磁盘缓存。当这个属性的值为true时,系统在初始化的时候会在磁盘中查找文件名为cache名称,后缀名为index的的文件,如CACHE_FUNC.index 。这个文件中存放了已经持久化在磁盘中的cache的index,找到后把cache加载到内存。要想把cache真正持久化到磁盘,写程序时必须注意,在是用net.sf.ehcache.Cache的void put (Element element)方法后要使用void flush()方法。
5、CacheUtils.java
1 /** 2 * Cache工具类 3 */ 4 public class CacheUtils { 5 6 private static CacheManager cacheManager = ((CacheManager)SpringContext.getBean("cacheManager")); 7 8 private static final String SYS_CACHE = "sysCache"; 9 10 /** 11 * 获取SYS_CACHE缓存 12 * @param key 13 * @return 14 */ 15 public static Object get(String key) { 16 return get(SYS_CACHE, key); 17 } 18 19 /** 20 * 写入SYS_CACHE缓存 21 * @param key 22 * @return 23 */ 24 public static void put(String key, Object value) { 25 put(SYS_CACHE, key, value); 26 } 27 28 /** 29 * 从SYS_CACHE缓存中移除 30 * @param key 31 * @return 32 */ 33 public static void remove(String key) { 34 remove(SYS_CACHE, key); 35 } 36 37 /** 38 * 获取缓存 39 * @param cacheName 40 * @param key 41 * @return 42 */ 43 public static Object get(String cacheName, String key) { 44 Element element = getCache(cacheName).get(key); 45 return element==null?null:element.getObjectValue(); 46 } 47 48 /** 49 * 写入缓存 50 * @param cacheName 51 * @param key 52 * @param value 53 */ 54 public static void put(String cacheName, String key, Object value) { 55 Element element = new Element(key, value); 56 getCache(cacheName).put(element); 57 } 58 59 /** 60 * 从缓存中移除 61 * @param cacheName 62 * @param key 63 */ 64 public static void remove(String cacheName, String key) { 65 getCache(cacheName).remove(key); 66 } 67 68 /** 69 * 获得一个Cache,没有则创建一个。 70 * @param cacheName 71 * @return 72 */ 73 private static Cache getCache(String cacheName){ 74 Cache cache = cacheManager.getCache(cacheName); 75 if (cache == null){ 76 cacheManager.addCache(cacheName); 77 cache = cacheManager.getCache(cacheName); 78 cache.getCacheConfiguration().setEternal(true); 79 } 80 return cache; 81 } 82 83 public static CacheManager getCacheManager() { 84 return cacheManager; 85 } 86 87 }
6、SpringContext.java,用类获得ApplicationContext实例
1 @Component 2 public class SpringContext implements ApplicationContextAware,DisposableBean{ 3 private static ApplicationContext applicationContext; 4 /** 5 * 取得存储在静态变量中的ApplicationContext. 6 */ 7 @Override 8 public void setApplicationContext(ApplicationContext context) 9 throws BeansException { 10 try { 11 applicationContext=context; 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 } 16 /** 17 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. 18 */ 19 public static <T> T getBean(String name){ 20 return (T) applicationContext.getBean(name); 21 } 22 public static String[] getBeanNamesForType(Class<?> type){ 23 return applicationContext.getBeanNamesForType(type); 24 } 25 /** 26 * 实现DisposableBean接口, 在Context关闭时清理静态变量. 27 */ 28 @Override 29 public void destroy() throws Exception { 30 applicationContext = null; 31 } 32 }
7、UserUtils.java
1 /** 2 * 用户工具类 3 */ 4 public class UserUtils { 5 6 7 private static UserDao userDao = SpringContextHolder.getBean(UserDao.class); 8 private static Map<String, Object> cacheMap; 9 /** 10 * 根据ID获取用户 11 * @param id 12 * @return 取不到返回null 13 */ 14 public static User getUser(String id){ 15 User user = (User)CacheUtils.get("userCache", "id_" + id); 16 if (user == null){ 17 user = userDao.get(id); 18 if (user == null){ 19 return null; 20 } 21 CacheUtils.put("userCache", "id_" + user.getId(), user); 22 CacheUtils.put("userCache", "ln" + user.getLoginName(), user); 23 } 24 return user; 25 } 26 27 /** 28 * 根据登录名获取用户 29 * @param loginName 30 * @return 取不到返回null 31 */ 32 public static User getByLoginName(String loginName){ 33 User user = (User)CacheUtils.get("userCache", "ln" + loginName); 34 if (user == null){ 35 user = userDao.getByLoginName(new User(null, loginName)); 36 if (user == null){ 37 return null; 38 } 39 CacheUtils.put("userCache", "id_" + user.getId(), user); 40 CacheUtils.put("userCache", "ln" + user.getLoginName(), user); 41 } 42 return user; 43 } 44 45 46 /** 47 * 清除用户缓存 48 * @param user 49 */ 50 public static void clearCache(User user){ 51 CacheUtils.remove("userCache", "id_" + user.getId()); 52 CacheUtils.remove("userCache", "ln" + user.getLoginName()); 53 CacheUtils.remove("userCache", "ln" + user.getOldLoginName()); 54 } 55 }