在上一篇涉及到查询缓存的功能时除了需要在配置文件中开启缓存外,还需要在业务代码中显示调用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. openssl使用多种方法签名、自签名

    1.自建CA 自建CA的机制:1.生成私钥2.创建证书请求,在创建证书请求过程中由于需要提供公钥,而公钥来源于私钥,所以也需要指定私钥来创建证书请求,而实际上这里提供私钥的作用就是提取其中的公钥,这一 ...

  2. RabbitMQ 入门指南(Java)

    RabbitMQ是一个受欢迎的消息代理,通常用于应用程序之间或者程序的不同组件之间通过消息来进行集成.本文简单介绍了如何使用 RabbitMQ,假定你已经配置好了rabbitmq服务器. Rabbit ...

  3. python基础知识8——模块1——自定义模块和第三方开源模块

    模块的认识 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需 ...

  4. sql server2008 字段类型

    bit    整型 bit数据类型是整型,其值只能是0.1或空值.这种数据类型用于存储只有两种可能值的数据,如Yes 或No.True 或False .On 或Off. 注意:很省空间的一种数据类型, ...

  5. zTree入门-最简单的树

    最近发现项目中很多地方都是树形菜单,而这些树形菜单都是使用树形插件zTree来制作的,所以就想自学一下zTree,参照官方文档写了一个简单的案例,使用zTree做了 一个最简单的树形结构. 案例:zT ...

  6. Android_AsyncTaskDemo之QQ记步数(画圆形图片知识)

    今天学习了AsyncTask Android 的异步机制.我简单的实现我的一个小小案例--qq记步数.然后穿插一个画圆形图片的知识点. 由于所学知识有限,目前我计数,还有排名等等我就简单的利用随机数实 ...

  7. xpath爬取网页评论,网址的的调用方法,数据库特殊字符的替换

    # -*- coding:utf-8-*-from lxml import etreeimport urllibimport jsonimport requestsimport MySQLdbid=0 ...

  8. C# 从字符串中取出英文字母

    string fid = context.Request["value2"];//fid=FCT1234 Regex re = new Regex(@"[a-zA-Z]+ ...

  9. Android 7.0 UICC 分析(三)

    本文讲解UICCCardApplication /frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccC ...

  10. Python全栈之路目录结构

    基础 1.Python全栈之路-----基础篇 2.Python全栈之路---运算符与基本的数据结构 3.Python全栈之路3--set集合--三元运算--深浅拷贝--初识函数 4.Python全栈 ...