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关系的映射等 ...
随机推荐
- string.Join()的用法
List<string> list = new List<string>(); list.Add("I"); list.Add("Love&quo ...
- Modelica学习
Annotation Choices for Suggested Redeclarations and Modifications Replaceable model sample(start,int ...
- IDEA工具使用说明
IDEA使用说明 1.安装 2.开始界面 1)create New Project (新建项目) 2)Import Project (导入项目) 3)Open (打开已有的项目) 4)Check o ...
- select标签非空验证,第一个option value=""即可
select标签非空验证,第一个option value=""即可,否则不能验证
- android studio中如何设置注释模板
在开发程序的时候,我们一般都会给文件自动添加上一些关于文件的注释信息,比如开发者的名字,开发的时间,开发者的联系方式等等.那么在android studio中该如何设置呢? 工具/原料 andro ...
- 【项目】搜索广告CTR预估(二)
项目介绍 给定查询和用户信息后预测广告点击率 搜索广告是近年来互联网的主流营收来源之一.在搜索广告背后,一个关键技术就是点击率预测-----pCTR(predict the click-through ...
- jquery radio
取radio的值: JS代码 $("input[name='radioName'][checked]").val(); 给radio 赋值, 选中值为2的radio: JS代码 $ ...
- limux curl命令
linux curl命令很强大: http://blog.chinaunix.net/uid-14735472-id-3413867.html curl是一种命令行工具,作用是发出网络请求,然后得到和 ...
- HTTP Error 503. The service is unavailable
网站运行一段时间后,突然所有的页面都报告以下错误: HTTP Error 503. The service is unavailable 经检查,应用程序池自动停止,可能是工作进程抛出的异常数超出限制 ...
- MySQL数据库引擎介绍、区别、创建和性能测试的深入分析
本篇文章是对MySQL数据库引擎介绍.区别.创建和性能测试进行了详细的分析介绍,需要的朋友参考下 数据库引擎介绍 MySQL数据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎 ...