Hibernate缓存之Aop+cache
在上一篇涉及到查询缓存的功能时除了需要在配置文件中开启缓存外,还需要在业务代码中显示调用setCacheable(boolean)才可以打开查询缓存的功能,这样做,无疑是破坏了封装性,所以就诞生了利用AOP切面编程来实现查询缓存。原理:在执行查询操作之前根据Key(类名+方法名+参数列表)判断二级缓存中是否有,如果没有,则执行查询操作,并将结果保存到二级缓存中,如果有,则直接从二级缓存中获取数据。下面就通过两种方式来利用aop实现查询缓存的功能。
(一)、配置文件拦截器实现方式
cacheContext.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="myCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache.xml</value>
</property>
</bean> <!-- 定义ehCache的工厂,并设置所使用的Cache name -->
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="myCacheManager" />
</property>
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean> <!--cache拦截器 -->
<bean id="methodCacheInterceptor" class="com.myoracle.interceptor.MethodCacheInterceptor">
<property name="methodCache">
<ref local="ehCache" />
</property>
</bean> <!-- 以下针对MethodCacheInterceptor方法使用到的配置 -->
<bean id="methodCachePointCut"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor" />
</property>
<property name="patterns">
<list>
<value>.*search.*</value>
</list>
</property>
</bean>
</beans>
ehcache.xml放在src文件夹目录下面,同时在hibernate.cfg.xml中是否开启二级缓存都不影响以下代码执行效果(即使:<property name="hibernate.cache.use_second_level_cache">false</property>执行效果也一致)
applicationContext.xml中在上一篇的基础上加入以下配置代码
<import resource="cacheContext.xml" />
<aop:config proxy-target-class="true" />
<!-- 切面编程声明 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- spring annotation -->
<context:annotation-config /> <!-- 以下针对MethodCacheInterceptor方法使用到的配置 -->
<bean id="userServiceImpl" class="com.myoracle.service.UserServiceImpl" />
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="userServiceImpl" />
</property>
<property name="interceptorNames">
<list>
<value>methodCachePointCut</value>
</list>
</property>
</bean>
接着,编写代码拦截器的主要代码:
/**
* 和cacheContext.xml中的methodCacheInterceptor中一一对应
*/
public class MethodCacheInterceptor implements MethodInterceptor { //在cacheContext.xml中完成注入
private Cache methodCache; public Cache getMethodCache() {
return methodCache;
} public void setMethodCache(Cache methodCache) {
this.methodCache = methodCache;
} public MethodCacheInterceptor() {
super();
} public Object invoke(MethodInvocation invocation) throws Throwable {
try { System.out.println("*******进入拦截器执行******"); 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 = methodCache.get(cacheKey);
if (null == element) {
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
methodCache.put(element);
}
return element.getValue(); } catch (Exception e) {
System.out.println("拦截器中出现异常");
return new Object();
}
} private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
try {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((null != arguments) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
} catch (Exception e) {
System.out.println("拦截器getCacheKey中出现异常");
return "";
}
} }
执行效果如下:(此时是在屏蔽setCacheable(boolean)的情况下执行的,如果不加拦截器,正常的执行效果是发出两条sql语句)
(二)、注解方式拦截器实现方式
在上面的实现过程中明显感觉配置文件太繁琐,而且如果希望可以针对具体的类、方法进行拦截处理,在上面的配置文件中可能涉及到编写正则表达式,编写容易发生错误,所以想到可以自定义一个注解,在需要拦截的类或者方法中定义此注解即可实现拦截效果,而且也不需要大量的配置文件,所以下面可以称之为是简化版.....
首先定义一个注解,方式定义了该注解的方法和类均可以完成拦截
/**
* @Target指明作用的范围,针对方法和类
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {
int expire() default 0; // 过期时间
}
看主要的代码,注意,在cacheContext.xml中对于ehcache的声明还是需要的,因为在下面的代码中,需要注入ehcache
@Component("methodCacheAspectJ")
@Aspect
public class MethodCacheAspectJ {
private Cache cache; public Cache getCache() {
return cache;
}
//注意cacheContext.xml中的ehcahe的声明需要保留
@Resource(name = "ehCache")
public void setCache(Cache cache) {
this.cache = cache;
}
//定义方法切入点,凡是注解了MethodCache的类均被拦截
@Pointcut("@annotation(com.myoracle.annotation.MethodCache)")
public void methodCachePointcut() {
} @Around("methodCachePointcut()")
public Object methodCacheHold(ProceedingJoinPoint joinPoint)
throws Throwable { System.out.println("*************进入面向切面编程****************"); String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Object result = null;
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (null == element) {
try {
result = joinPoint.proceed();// 执行方法
} catch (Exception e) {
System.out.println("执行方法失败");
}
if (null != result) {
try {
element = new Element(cacheKey, (Serializable) result);
Class targetClass = Class.forName(targetName);
Method[] method = targetClass.getMethods();
int expire = 0;
for (Method m : method) {
if (m.getName().equals(methodName)) {
Class[] tmpCs = m.getParameterTypes();
if (tmpCs.length == arguments.length) {
MethodCache methodCache = m
.getAnnotation(MethodCache.class);
expire = methodCache.expire();
break;
}
}
}
if (expire > 0) {
element.setTimeToIdle(expire);
element.setTimeToLive(expire);
}
cache.put(element);
} catch (Exception e) {
System.out.println("cachekey=" + cacheKey + " 为执行缓存");
}
}
}
return element.getValue();
} private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((null != arguments) && (0 != arguments.length)) {
for (int i = 0; i < arguments.length; i++) {
if (arguments[i] instanceof Date) {
sb.append(".").append(((Date) arguments[i]).toString());
} else {
sb.append(".").append(arguments[i]);
}
}
}
return sb.toString();
} }
以上代码执行效果图:
可以看出同样达到了使用查询缓存的效果........
从上面代码中结合上一篇章中对查询缓存概念的讲解可以深入理解其实现原理,进一步了解缓存实现的机制.
Hibernate缓存之Aop+cache的更多相关文章
- 缓存机制总结(JVM内置缓存机制,MyBatis和Hibernate缓存机制,Redis缓存)
一.JVM内置缓存(值存放在JVM缓存中) 我们可以先了解一下Cookie,Session,和Cache Cookie:当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cooki ...
- Hibernate 缓存机制浅析
1. 为什么要用 Hibernate 缓存? Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源 ...
- hibernate缓存机制(转)
原文出处:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是 ...
- 【转】hibernate缓存:一级缓存和二级缓存
什么是缓存? 缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份临时放在内存中的容器,其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用程序的运行性能.Hibernate在进行 ...
- Hibernate缓存(转)
来自:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是一个 ...
- 初识Hibernate 缓存
生活就像一杯咖啡,让你我慢慢的品尝,品尝它的苦涩和甘甜...... 一.什么是Hibernate缓存. 解析:白话来说就是缓存数据的容器 官方标准点缓存:是计算机领域的概念,它介于应用程序和永久性数据 ...
- Hibernate缓存原理与策略
Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...
- [原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- Hibernate缓存原理与策略 Hibernate缓存原理:
Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...
随机推荐
- 文法 LL1
<程序>-><声明列表>|<程序><函数> <声明列表>-><声明>|<声明><声明列表> ...
- 一个Tomcat多端口多项目部署
为什么突然写这个呢,前两天面试了,被面试官给问住了,回来一查.....怎么这么简单,当时我就....你懂得. 我用的是Tomcat7,用Eclipse新建了两个简单的web项目,贴出web1的截图,w ...
- javascript-binarySearch
前提: 数组已排序,且为正整数数组. function brnarySearch(arg, arr) { var right = arr.length - 1; var left = 0; while ...
- RabbitMQ消息队列1: Detailed Introduction 详细介绍
1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...
- 16.2.2 Space Needed for keys
myisam表使用btree索引,可以粗略计算出索引文件的大小,使用(key_length+4)/0.67,全部key的总和,全部key被排序顺序插入和表没有被任何压缩的时候,这是最坏的情况 stri ...
- sql 2012 操作EXCEL 2013
确认是否有Microsoft.ACE.OLEDB.12.0数据接口 --进行配置: EXEC sp_configure 'show advanced options', 1 GO RECONFIGUR ...
- Android Volley完全解析(一),初识Volley的基本用法
1. Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android 系统中主要提供了两种方式来进行 ...
- Java 字符串拼接 五种方法的性能比较分析 从执行100次到90万次
[请尊重原创版权,如需引用,请注明来源及地址] > 字符串拼接一般使用“+”,但是“+”不能满足大批量数据的处理,Java中有以下五种方法处理字符串拼接,各有优缺点,程序开发应选择合适的方法实现 ...
- fedora23 tweak tool不工作解决方案
在启动器中打开 优化工具 失败 在终端中开启显示 Traceback (most recent call last): File "/usr/bin/gnome-tweak-tool&quo ...
- web.py+html+mysql实现web端小系统的问题汇总
利用web.py+html(bootstrap)+mysql实现了一个小型的设备管理系统,在这个过程中遇到很多问题,将问题及解决方案总结如下,有遇到类似问题的同学,希望可以帮到你们. 1.关于中文的编 ...