在做系统集成平台项目的时候遇到了一个比較麻烦的问题。原因是使用考试系统的时候所依赖的是基础系统公布的webservice来获取基础数据,webservice的跨网络传输本身或多或少会对系统性能产生一定影响再加上传输的数据量比較大这样对系统性能的影响就更大了,可是导致系统性能下降的另外一个原因就是频繁的打开关闭数据库。针对这两个问题我们採取了两个解决方式以期将性能影响降至最低第一就是webservice由原先的传输序列化对象改为传输json串,第二个就是针对数据库连接的开闭问题作了缓存处理。本文我们主要探讨第二个解决方式ehcache。

ehcache是一个很不错的缓存框架,配置前来简单而且功能强大,在项目中加缓存的地方主要有两处,第一是缓存实体对象。这层缓存加在实体层,主要使用的是hibernate的二级缓存(同一时候一定要开启查询缓存)利用spring的AOP注解就可以简单搞定,而在其它查询方法上主要用的就是ehcache,用来缓存方法返回的各种对象。开启hibernate的查询缓存和二级缓存比較简单。在此不做过多介绍,我们主要来看ehcache的使用方法。

1.首先我们用到的是Interceptor,定义两个拦截器MethodCacheInterceptor和MethodCacheAfterAdvice,前者主要用来拦截以get和find开头的方法(用于缓存结果)。而第二个拦截器主要用来拦截以update开头的方法,用来清除缓存。以下让我们来看一下详细的代码:

public class MethodCacheInterceptor implements MethodInterceptor,
InitializingBean {
private static final Log logger = LogFactory
.getLog(MethodCacheInterceptor.class); private Cache cache; public void setCache(Cache cache) {
this.cache = cache;
} public MethodCacheInterceptor() {
super();
} /**
* 拦截Service/DAO 的方法,并查找该结果是否存在,假设存在就返回cache 中的值, 31 *
* 否则,返回数据库查询结果。并将查询结果放入cache 32
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result; logger.debug("Find object from cache is " + cache.getName()); String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey); if (element == null) {
logger
.debug("Hold up method , Get method result and create cache........!");
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
System.out.println("-----非缓存中查找。查找后放入缓存");
cache.put(element);
}else{
System.out.println("----缓存中查找----");
}
return element.getValue();
} /**
* 获得cache key 的方法,cache key 是Cache 中一个Element 的唯一标识 55 * cache key
* 包含包名+类名+方法名,如 com.co.cache.service.UserServiceImpl.getAllUser 56
*/
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();
} /**
* implement InitializingBean。检查cache 是否为空 70
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
} }

第二个拦截器的代码例如以下:

public class MethodCacheAfterAdvice implements AfterReturningAdvice,
InitializingBean {
private static final Log logger = LogFactory
.getLog(MethodCacheAfterAdvice.class); private Cache cache; public void setCache(Cache cache) {
this.cache = cache;
} public MethodCacheAfterAdvice() {
super();
} public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for (int i = 0; i < list.size(); i++) {
String cacheKey = String.valueOf(list.get(i));
if (cacheKey.startsWith(className)) {
cache.remove(cacheKey);
System.out.println("------清除缓存----");
logger.debug("remove cache " + cacheKey);
}
}
} public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
} }

有了这两个拦截器,接下来我们所要做的就是为将这两个拦截器引入进项目让其发挥作用,这些配置都在ehcache.xml文件里进行,以下来看该文件的详细配置:

<?xml version="1.0" encoding="UTF-8"?

>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 引用ehCache 的配置-->
<bean id="defaultCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>ehcache.xml</value>
</property>
</bean> <!-- 定义ehCache 的工厂,并设置所使用的Cache name -->
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="defaultCacheManager" />
</property>
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean> <!-- find/create cache 拦截器-->
<bean id="methodCacheInterceptor" class="com.co.cache.ehcache.MethodCacheInterceptor">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<!-- flush cache 拦截器-->
<bean id="methodCacheAfterAdvice" class="com.co.cache.ehcache.MethodCacheAfterAdvice">
<property name="cache">
<ref local="ehCache" />
</property>
</bean> <bean id="methodCachePointCut"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor" />
</property>
<property name="patterns">
<list>
<value>.*find.*</value>
<value>.*get.*</value>
</list>
</property>
</bean>
<bean id="methodCachePointCutAdvice"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheAfterAdvice" />
</property>
<property name="patterns">
<list>
<value>.*create.*</value>
<value>.*update.*</value>
<value>.*delete.*</value>
</list>
</property>
</bean>
</beans>

这样就将拦截器的配置以及缓存配置引入导了项目中,缓存配置信息主要放在ehcache.xml文件里,具体信息例如以下:

<ehcache>
<diskStore path="H:\\temp\\cache" />
<defaultCache maxElementsInMemory="1000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" />
<cache name="DEFAULT_CACHE" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="300000" timeToLiveSeconds="600000" overflowToDisk="true" />
</ehcache>

至此我们的对应配置都已经做好了,以下让我们建立測试类来測试缓存是否起作用,在这里我们主要用的类有三个,来看详细代码:

public interface TestService {
public List getAllObject(); public void updateObject(Object Object);
}

TestService是调用接口,而以下的TestServiceImpl是事实上现,代码例如以下:

public class TestServiceImpl implements TestService {
public List getAllObject() {
System.out.println("---TestService:Cache 内不存在该element。查找并放入Cache。");
return null;
} public void updateObject(Object Object) {
System.out.println("---TestService:更新了对象,这个Class 产生的cache 都将被remove!");
}
}

以下的JunitTestClass为真正的測试类。代码例如以下:

public class JunitTestClass {

	@Test
public void testRun(){
String DEFAULT_CONTEXT_FILE = "/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(
DEFAULT_CONTEXT_FILE);
TestService testService = (TestService) context.getBean("testService"); //第一次查找
testService.getAllObject(); //第二次查找
testService.getAllObject(); //运行update方法(应该清除缓存)
testService.updateObject(null); //第三次查找
testService.getAllObject();
}
}

分析測试代码,当第一次运行getAllObject()方法的时候因为是第一次运行查询操作,会被MethodCacheInterceptor拦截。当MethodCacheInterceptor发现没有命中缓存的时候,运行invoke()方法。让程序去数据库查询(本程序中仅仅是模拟了对数据库的查询,并没有真正查询数据库。只是其所表达的意思是与查询数据库没有差别的),我们看到这是会运行TestServiceImpl的getAllObject()方法,打印出一条语句同一时候打印拦截器中的“-----非缓存中查找。查找后放入缓存”语句。当第二次运行该方法的时候因为已经存在了缓存,所以不再运行TestServiceImpl的getAllObject()方法。同一时候仅仅打印拦截器中的“----缓存中查找----”语句,当运行updateObject()方法的时候会被MethodCacheAfterAdvice拦截,并运行TestServiceImpl的updateObject()方法,所以会打印“---TestService:更新了对象。这个Class
产生的cache 都将被remove”语句以及拦截器中的“------删除缓存----”语句,当运行第三次查找的时候。因为缓存已经被清除,所以会在此输出和第一次一样的语句,以下来验证一下我们的猜測是否正确:

输出结果与我们推測的一样,也就是说此时ehcache缓存在程序中已经起作用了。

spring+ehcache实战--性能优化之道的更多相关文章

  1. for循环实战性能优化之使用Map集合优化

           笔者在<for循环实战性能优化>中提出了五种提升for循环性能的优化策略,这次我们在其中嵌套循环优化小循环驱动大循环的基础上,借助Map集合高效的查询性能来优化嵌套for循环 ...

  2. 使用Spring Ehcache二级缓存优化查询性能

    最近在对系统进行优化的时候,发现有些查询查询效率比较慢,耗时比较长, 通过压测发现,主要耗费的性能 消耗在 查询数据库,查询redis 数据库:连接池有限,且单个查询不能消耗大量的连接池,占用大量IO ...

  3. MongoDB实战性能优化

    1. 性能优化分类 mongodb性能优化分为软件层面和操作系统层面. 软件层面,一般通过修改mongodb软件配置参数来达到,这个需要非常熟悉mongodb里面的各种配置参数: 而操作系统层面,相对 ...

  4. MySQL性能优化之道

    1.in和not in子查询优化 not in 是不能命中索引的,所以以下子查询性能很低. 如果是确定且有限的集合时,可以使用.如 IN (0,1,2). 用 exists或 notexists代替 ...

  5. 【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战

    一.写在前面   相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这是非常火的一门技术. 如果只是用户量很少的传统IT系统,使用Spring Cloud可能还暴露不出 ...

  6. Spring/Hibernate 应用性能优化的7种方法

    对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能.此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法.本文系 O ...

  7. for循环实战性能优化

    完成同样的功能,用不同的代码来实现,性能上可能会有比较大的差别,所以对于一些性能敏感的模块来说,对代码进行一定的优化还是很有必要的.今天就来说一下java代码优化的事情,今天主要聊一下对于for(wh ...

  8. Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势

    原创文章,同步首发自作者个人博客转载请务必在文章开头处注明出处. 摘要 本文结合实例详细阐明了Spark数据倾斜的几种场景以及对应的解决方案,包括避免数据源倾斜,调整并行度,使用自定义Partitio ...

  9. Spring Cloud Feign 性能优化

    #### 1.替换 tomcat 首先,把 tomcat 换成 undertow,这个性能在 Jmeter 的压测下,undertow 比 tomcat 高一倍 **第一步,pom 修改去除tomca ...

随机推荐

  1. SDL2中文教程

    SDL2.0 Tutorial Index 原文地址:SDL 2.0 Tutorial Index Welcome! 下面的教程旨在为你提供一个SDL2.0以及c++中游戏设计和相关概念的介绍.在本教 ...

  2. 【HBase】Rowkey设计【转】

    本章将深入介绍由HBase的存储架构在设计上带来的影响.如何设计表.row key.column等等,尽可能地使用到HBase存储上的优势. Key设计 HBase有两个基础的主键结构:row key ...

  3. singer页面点击歌手singer是跳转到singer-detail的设置

    1.创建components/singer-detail/singer-detail.vue 2.配置动态路由: { path: ':id', name:'singer-detail', compon ...

  4. 给class添加id封装

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Java类型Float&&Double

    package study; public class testFloatDouble { public static void main(String[] args) { float f = 0; ...

  6. 【转】MapReduce读取lzo文件

    1.读lzo文件 需要添加以下代码,并导入lzo相关的jar包 job.setInputFormatClass(LzoTextInputFormat.class); 2.写lzo文件 lzo格式默认是 ...

  7. RTX——第14章 信号量

    以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 本章节开始讲解 RTX 的另一个重要的任务间的同步和资源共享机制,信号量. 信号量有3种用途: 1) 表 ...

  8. c++重载前置++和--

    C语言中,前置和后置++,--都不能作为左值,而在c++中,前置的++和--可以作为左值,从下面的重载运算符中也可以看出,它们返回的是引用,我不知道为什么这里和c语言中不同,但c++类似的提升还有三目 ...

  9. redis使用redis-cli查看所有的keys及清空所有的数据

    redis_home:redis安装路径: cd %redis_home%/src ./redis-cli -h 127.0.0.1   127.0.0.1:6379> keys *   (em ...

  10. IIS安全加固

    1 删除IIS默认站点 把IIS默认安装的站点删除或禁用掉. 2 禁用不必要的Web服务扩展 打开IIS 管理器,检查是否有不必要的“Web服务扩展”,如果有则禁用掉.如下图所示: 3 IIS访问权限 ...