1 spring与ehcache结合使用,需要导入如下的包:ehcache , commons-logging , cglib , asm , spring的jar包具体版本可以选择最新。
2 spring配置文件applicationContext.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" default-lazy-init="true"> <!-- 在spring里配置cache就和在spring配置数据库一样, --> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>ehcache.xml</value> </property> </bean> <bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="cacheManager"/> </property> <property name="cacheName"> <value>com.rmn190.MethodCache</value> </property> </bean> <bean id="methodCacheInterceptor" class="intercepter.MethodCacheInterceptor"> <property name="cache"> <ref local="methodCache"/> </property> </bean> <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="methodCacheInterceptor"/> </property> <!-- 下面的配置就使得在数据访问时,cache将拦截从数据库获取的数据,与cache数据比较,如有就不放入cache,没有就放入,更新到数据库去,也是先存入cache,再更新到数据库中去 --> <property name="patterns"> <list> <value>.*getList</value> </list> </property> </bean> <bean id = "personManager" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <!--<bean class="manager.PersonManagerImpl"/>--> <ref local="personManagerTarget"/> </property> <property name="interceptorNames"> <list> <value>methodCachePointCut</value> </list> </property> </bean> <bean id = "personManagerTarget" class="manager.PersonManagerImpl"/> </beans>
ehcache配置文件ehcache.xml如下:
<ehcache> <!-- Sets the path to the directory where cache .data files are created. If the path is a Java System Property it is replaced by its value in the running VM. The following properties are translated: user.home - User's home directory user.dir - User's current working directory java.io.tmpdir - Default temp file path --> <diskStore path="java.io.tmpdir"/> <!--Default Cache configuration. These will applied to caches programmatically created through the CacheManager. The following attributes are required: maxElementsInMemory - Sets the maximum number of objects that will be created in memory eternal(永恒) - Sets whether elements are eternal. If eternal, timeouts are ignored and the element is never expired(过期). overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache has reached the maxInMemory limit. The following attributes are optional: timeToIdleSeconds - Sets the time to idle for an element before it expires. i.e. The maximum amount of time between accesses before an element expires Is only used if the element is not eternal. Optional attribute. A value of 0 means that an Element can idle for infinity. The default value is 0. timeToLiveSeconds - Sets the time to live for an element before it expires. i.e. The maximum time between creation time and when an element expires. Is only used if the element is not eternal. Optional attribute. A value of 0 means that and Element can live for infinity. The default value is 0. diskPersistent - Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value is 120 seconds. --> <!-- maxElementsInMemory设定内存中创建对象的最大值 --> <!-- eternal设置元素(译注:内存中对象)是否永久驻留。如果是,将忽略超 时限制且元素永不消亡。--> <!-- overflowToDisk设置当内存中缓存达到 maxInMemory 限制时元素是否可写到磁盘上 --> <!-- timeToIdleSeconds设置某个元素消亡前的停顿时间。 也就是在一个元素消亡之前,两次访问时间的最大时间间隔值。 这只能在元素不是永久驻留时有效(译注:如果对象永恒不灭,则 设置该属性也无用)。 如果该值是 0 就意味着元素可以停顿无穷长的时间。 --> <!-- timeToLiveSeconds为元素设置消亡前的生存时间。 也就是一个元素从构建到消亡的最大时间间隔值。 这只能在元素不是永久驻留时有效。 --> <!-- diskPersistent是否disk store在虚拟机启动时持久化。默认为false --> <!-- diskExpiryThreadIntervalSeconds运行disk终结线程的时间,默认为120秒 --> <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="500" timeToLiveSeconds="1000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"/> <cache name="com.rmn190.MethodCache" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="200" timeToLiveSeconds="300" overflowToDisk="true" /> </ehcache>
4 测试类如下:
主类:
package main; import java.util.List; import manager.PersonManagerImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /* * 当没有导入cglib的jar包时,会抛出如下的异常: * 信息: Initializing EHCache CacheManager Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personManager': FactoryBean threw exception on object creation; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces. Caused by: org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces. at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:65) at org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy(ProxyCreatorSupport.java:106) at org.springframework.aop.framework.ProxyFactoryBean.getSingletonInstance(ProxyFactoryBean.java:297) at org.springframework.aop.framework.ProxyFactoryBean.getObject(ProxyFactoryBean.java:227) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectFromFactoryBean(AbstractBeanFactory.java:1236) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1207) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:262) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:733) at main.HelloEhcacheSpring.main(HelloEhcacheSpring.java:15) */ /* * 加入cglib,而没加入asm的jar包时,抛出如下异常: * 信息: Initializing EHCache CacheManager Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personManager': FactoryBean threw exception on object creation; nested exception is java.lang.NoClassDefFoundError: org/objectweb/asm/Type Caused by: java.lang.NoClassDefFoundError: org/objectweb/asm/Type at net.sf.cglib.core.TypeUtils.parseType(TypeUtils.java:180) at net.sf.cglib.core.KeyFactory.<clinit>(KeyFactory.java:66) at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69) at org.springframework.aop.framework.Cglib2AopProxy.createEnhancer(Cglib2AopProxy.java:224) at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:151) at org.springframework.aop.framework.ProxyFactoryBean.getProxy(ProxyFactoryBean.java:342) at org.springframework.aop.framework.ProxyFactoryBean.getSingletonInstance(ProxyFactoryBean.java:297) at org.springframework.aop.framework.ProxyFactoryBean.getObject(ProxyFactoryBean.java:227) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectFromFactoryBean(AbstractBeanFactory.java:1236) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1207) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:262) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:733) at main.HelloEhcacheSpring.main(HelloEhcacheSpring.java:32) Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:252) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 14 more */ /* * 这里使用了ehcache与spring结合,这里并没用用到数据库,用spring只是用来管理bean,这里用ehcache就相当于数据库,存放对象信息 */ @SuppressWarnings({"unchecked"}) public class HelloEhcacheSpring { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); PersonManagerImpl personManager = (PersonManagerImpl) context.getBean("personManager");//配置了spring就可以从配置文件里找到对应的接口实现类,再生成实例对象,以完成业务处理 for(int i=0;i<5;i++) { showPersonsInfo(personManager); } } private static void showPersonsInfo(PersonManagerImpl personManager) { //要是没有cache时,那么这里会直接从PersonmanagerImpl类的实例对象获取到数据,这里配置了cache后,就会先跳到cache去更新cache,再往下执行 List<String> persons = personManager.getList(); for(String person : persons) { System.out.println(person); } } }
数据访问时,触发ehcache的更新:
package intercepter; import java.io.Serializable; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.factory.InitializingBean; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean{ private Cache cache; public void setCache(Cache cache) { this.cache = cache; } /* * 首先有一点要明白的是:invoke的触发都在由于DAO或sevlet的数据访问时才会调用到 */ //invoke方法会在spring配置文件里的,指明的cache拦截的方法的调用时,自动触发它,如这个项目里,当运行HelloEhcacheSpring.java类时,在showPersonsInfo方法里调用到personManager.getList()方法时,它就会先调到这里来执行,执行完才行下执行它的业务 public Object invoke(MethodInvocation invocation) throws Throwable { String targetName = invocation.getThis().getClass().getName();//这个表示哪个类调用(或触发)了这个MethodCacheInterceptor,如里的:manager.PersonMagagerImpl String methodName = invocation.getMethod().getName();//这个表示哪个方法触发了这个类(MethodCacheInterceptor)方法(invoke)的调用,如这里的:getList Object[] arguments = invocation.getArguments();//调用的参数,这里没有参数 Object result; String cacheKey = getCacheKey(targetName, methodName, arguments);//这里得出的是:manager.PersonManagerImpl.getList Element element = cache.get(cacheKey); if (element == null) { // call target/sub-interceptor result = invocation.proceed();//这个就是调用数据访问方法,如这里是调用manager.PersonManagerImpl.getList(),并用result保存执行的结果(数据访问的结果),如这里调用了getList()方法,会先打印出"get Person from DB" ,然后将结果集放入到result里面去,这里由于使用的是自己配置只能放入10个元素的ehcache,所以这里的result是ArrayList<E> ,它里面存放的是elementData[10],并将getList得到的结果放入到elementData里面去了 System.out.println("set into cache"); // cache method result //下面方法执行后,将cacheKey与数据集连起来,cacheKey是用来标识这个element的标志,我们可以有多个element(各自是来自不同的数据访问方法而形成的),区分它们就是用cacheKey, element = new Element(cacheKey, (Serializable) result);//这里的新生成后的element,含有cacheKey,还在element创建时间,访问时间,还有命令次数等cache的属性,我觉得它就像是一个小cache一样,下次要不要更新它就要看它的这些属性来决定。 cache.put(element);//放入cache中 } System.out.println("out cache");//完成cache操作 return element.getValue(); } 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(); } public void afterPropertiesSet() throws Exception { if(null == cache) { throw new IllegalArgumentException("Cache should not be null."); } } }
数据提供类:
package manager; import java.util.ArrayList; import java.util.List; @SuppressWarnings("unchecked") public class PersonManagerImpl { private static List persons; static { persons = new ArrayList(); persons.add("Wang"); persons.add("zang"); persons.add("Li"); persons.add("song"); persons.add("yan"); } public List getList() { System.out.println("getPerons from DB"); return persons; } }