在上一篇涉及到查询缓存的功能时除了需要在配置文件中开启缓存外,还需要在业务代码中显示调用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的更多相关文章

  1. 缓存机制总结(JVM内置缓存机制,MyBatis和Hibernate缓存机制,Redis缓存)

    一.JVM内置缓存(值存放在JVM缓存中) 我们可以先了解一下Cookie,Session,和Cache Cookie:当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cooki ...

  2. Hibernate 缓存机制浅析

    1. 为什么要用 Hibernate 缓存? Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源 ...

  3. hibernate缓存机制(转)

    原文出处:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是 ...

  4. 【转】hibernate缓存:一级缓存和二级缓存

    什么是缓存? 缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份临时放在内存中的容器,其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用程序的运行性能.Hibernate在进行 ...

  5. Hibernate缓存(转)

    来自:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是一个 ...

  6. 初识Hibernate 缓存

    生活就像一杯咖啡,让你我慢慢的品尝,品尝它的苦涩和甘甜...... 一.什么是Hibernate缓存. 解析:白话来说就是缓存数据的容器 官方标准点缓存:是计算机领域的概念,它介于应用程序和永久性数据 ...

  7. Hibernate缓存原理与策略

    Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...

  8. [原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. Hibernate缓存原理与策略 Hibernate缓存原理:

    Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...

随机推荐

  1. scrum.4

    1.准备看板. 形式参考图4. 2.任务认领,并把认领人标注在看板上的任务标签上. 先由个人主动领任务,PM根据具体情况进行任务的平衡. 然后每个人都着手实现自己的任务. 3.为了团队合作愉快进展顺利 ...

  2. shell脚本的执行方式

    编写好的shell脚本(如:test),可以采取两种方式进行运行: 一. $ sh test 一般不采用这种调用方式,尤其不采用"sh<test"的调用方式,因为这种方式将禁 ...

  3. CSS3常用选择器(三)

    在CSS3的选择器中,除了结构性伪类选择器外,还有一种UI元素状态伪类选择器.这些选择器的共同特征: 指定的样式只有当元素处于某种状态时才起作用,在默认状态下不起作用. 1.hover.focus.a ...

  4. ZipFile解压文件不改变压缩包内文件修改日期的方法

    本文参考http://stackoverflow.com/questions/9813243/extract-files-from-zip-file-and-retain-mod-date-pytho ...

  5. android 术语

    Context : 是android 应用程序的 中央控制中心.所有应用程序特有的功能通过context 进行访问. Activity: 一个 Android 应用有若干个 task 任务组成,每个人 ...

  6. AngularJS作出简单聊天机器人

    简单聊天机器人 很初级的对话框形式.以前做对话框使用js,今天尝试使用AngularJS做出来 这里直接使用自己写的JSON数据. <!DOCTYPE html> <html lan ...

  7. Halcon学习之条形码实时扫描

    dev_open_window(1,1,400,400,'blue',ThisHandle) create_bar_code_model([], [], BarCodeHandle) set_bar_ ...

  8. 更改XAMPP中MySQL数据库的端口号

    更改XAMPP中MySQL数据库的端口号 如果电脑上已安装MySql数据库,还想用XAMPP中自带的数据库就需要更改XAMPP中数据库的端口号,避免和已安装的数据库冲突.本例以更改为3307端口号为例 ...

  9. linux下编译bib、tex生成pdf文件

    实验: 在linux环境下,编译(英文)*.bib和*.tex文件,生成pdf文件. 环境: fedora 20(uname -a : Linux localhost.localdomain 3.19 ...

  10. wkhtmltopdf中文显示空白或者乱码方框

    中文乱码或者空白解决方法 如果wkhtmltopdf中文显示空白或者乱码方框 打开windows c:\Windows\fonts\simsun.ttc拷贝到linux服务器/usr/share/fo ...