Hibeate缓存之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="pattes"><list><value>.*search.*</value></list></property></bean></beans>
ehcache.xml放在src文件夹目录下面,同时在hibeate.cfg.xml中是否开启二级缓存都不影响以下代码执行效果(即使:<property name="hibeate.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() {retu 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);}retu element.getValue();} catch (Exception e) {System.out.println("拦截器中出现异常");retu 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]);}}retu sb.toString();} catch (Exception e) {System.out.println("拦截器getCacheKey中出现异常");retu "";}}}
执行效果如下:(此时是在屏蔽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")@Aspectpublic class MethodCacheAspectJ {private Cache cache;public Cache getCache() {retu 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 + " 为执行缓存");}}}retu 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]);}}}retu sb.toString();}}
以上代码执行效果图:
可以看出同样达到了使用查询缓存的效果........
从上面代码中结合上一篇章中对查询缓存概念的讲解可以深入理解其实现原理,进一步了解缓存实现的机制.
作者:勇者归来
来源链接:https://www.cnblogs.com/wangyong/p/3408602.html
版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。
2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。