`
ahuaxuan
  • 浏览: 639676 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

aop cache再讨论

阅读更多
/**
*作者:张荣华
*日期:2008-11-07
**/

开门见山,一刀见血,让我们说说烦人的aop cache.

aop cache解释使用aop技术的cache,可以cache被代理对象的方法返回结果,还可以通过方法的参数值来控制缓存的粒度,看上去很美,用的人估计也颇多,好东西啊,面试的时候经常有人告诉我"我用过aop cache",看来是居家必备啊.不过居家必备的东西也得升个级什么滴啊,就想汽车一样,每年拉一次皮,照卖,还自夸是新一袋.aop cache要升级得先看看它烦人得地方.看看它烦人得地方先得知道它得用法,那么就先简单介绍一下它得用法:

常见步骤,2步
1,建立一个拦截器类,环绕增强或者后增强都可以,代码如下:
/**
 * @author ahuaxuan(aaron zhang) 代码原主是一个老外,不是我
 * @since 2008-5-13
 * @version $Id: MethodCacheInterceptor.java 814 2008-05-13 06:52:54Z aaron $
 */
@Component("methodCacheInterceptor")
@GlobalAutowired//这个是俺写的globalautowired,大家可以忽略
public class MethodCacheInterceptor implements MethodInterceptor {

	private Cache methodCache;

	public void setMethodCache(Cache methodCache) {
		this.methodCache = methodCache;
	}

	public Object invoke(MethodInvocation invocation) throws Throwable {
		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 (element == null) {
			result = invocation.proceed();

			element = new Element(cacheKey, (Serializable) result);
			methodCache.put(element);
		}
		return element.getValue();
	}

	private String getCacheKey(String targetName, String methodName,
			Object[] arguments) {
		StringBuffer sb = new StringBuffer();
		sb.append(targetName).append(".").append(methodName);
		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(arguments[i]);
			}
		}

		return sb.toString();
	}

}


这段代码很简单,就是缓存某个方法的返回结果,使用的缓存组件是ehcache,ehcache的比较详细的用法ahuaxuan在http://www.iteye.com/topic/128458这篇文章中已经有了说明.

而配置自动代理:
<!-- method cache auto proxy, add by ahuaxuan -->
     <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
         <property name="beanNames">
              <list>
                   <value>aaComponent</value>
                   <value>bbComponent</value>
              </list>
         </property>
         <property name="interceptorNames">
              <list>
                   <value>methodCacheInterceptor</value>
              </list>
         </property>
     </bean>

Over,最简单的aop cache.使用了该aop cache之后,可以缓存方法返回结果于无形,又可以根据方法参数来控制缓存粒度, 实乃居家旅行,杀人越货的必备良药

那么接下来看看这个用法有没有什么问题,相信熟悉一点的童子一眼就看出来了:”糟了,aaComponent和bbComponent所有的方法都被拦截了”.这个代码着实让我焦虑,我很焦虑.

Ok,我改,我改正则表达式还不行吗,我可以通过正则表达式让某些特定方法名的方法才被拦截处理.好啊,正统的spring用法,于是advice变成了advisor,增强变成了增强器, 但是我怎么看着就这么扭呢,难道我要缓存一个方法的结果还非得把这个方法的名字按照某个固定的格式来取, 再着,两个get方法,一个getxxx(),一个getyyy,两个之中一个需要缓存,另外一个不需要缓存(靠,真是变态),怎么办呢?正则的方式让我很烦躁,非常烦躁.

第一种方法让我焦虑,而第二种方法让我烦躁,我应该去寻找解决焦虑和烦躁的方案.

写代码需要有灵感,也需要有很强的分析能力,我们来看看我的问题是什么:
问题重新描述:不能精确的控制某个对象的某个方法需要被缓存.
思考:如何固定这个方法的标示-------------------
hardcode方法名到methodinterceptor中
hardcode缓存标示到方法上(如果该类所有方法都需要被缓存,那么hardcode缓存标示到类上)

我选第二种.那么看看实现步骤:
1.annotation类,两个:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {

}
还有一个:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCache {

}

看上去是多么无聊的两个annotation.

2修改methodinterceptor,加上判断逻辑
如果被代理的类加了ObjectCache,那么拦截这个对象所有的方法,如果没有类上没有加ObjectCache,那么判断method上有没有加methodcache,如果加了,拦截该方法,如果没有加,直接调用目标类的方法.

于是代码变成:
public Object invoke(MethodInvocation invocation) throws Throwable {
		
		String targetName = invocation.getThis().getClass().getInterfaces()[0].getName();
		String methodName = invocation.getMethod().getName();
		Object[] arguments = invocation.getArguments();
		
		if (invocation.getThis().getClass().isAnnotationPresent(ObjectCache.class)) {
			return getResult(targetName, methodName, arguments, invocation);
		} else {
			if (invocation.getMethod().isAnnotationPresent(MethodCache.class)) {
				return getResult(targetName, methodName, arguments, invocation);
			} else {
				return invocation.proceed();
			}
		}
	}

private Object getResult(String targetName, String methodName, Object[] arguments, MethodInvocation invocation) throws Throwable {
		Object result;
		
		String cacheKey = getCacheKey(targetName, methodName, arguments);
		Element element = methodCache.get(cacheKey);
		if (element == null) {
			result = invocation.proceed();

			element = new Element(cacheKey, (Serializable) result);
			methodCache.put(element);
		}
		
		return element.getValue();
	}

Ok,试试把,现在我要拦截aaservice上所有的方法,那么我的代码如下:
@ObjectCache
public class AaService  implement xxxxxx{

}


如果我要拦截bbservice上的b1方法,代码如下:
public class BbService implement xxxxxx{

	@MethodCache
	public void bb() {
		
	}
}


好了,目的达到了,我们可以任意的指定需要要拦截某个类的全部,或者部分方法了. 可是心中好像还是很闷的慌,我很慌张,非常慌张.
有人问了:都到这个份上了还慌啥张啊.
答:它tmd什么时候过期啊.我在ehcache.xml配置的可是统一的过期时间啊.ok,想到了,改,于是俺们的annotation就长成下面这个样子了:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {
	int expire() default 0;
}



@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCache {
	int expire() default 0;
}


再看看我们的进化过的methodInterceptor吧,大家可以详细比较一下下面这段和上面两端代码的异同之处
public Object invoke(MethodInvocation invocation) throws Throwable {
		
		String targetName = invocation.getThis().getClass().getInterfaces()[0].getName();
		String methodName = invocation.getMethod().getName();
		Object[] arguments = invocation.getArguments();
		Class[] cs = new Class[arguments.length];
		for (int k = 0; k < arguments.length; k++) {
			cs[k] = arguments[k].getClass();
		}
		
		if (invocation.getThis().getClass().getCanonicalName().contains("$Proxy")) {
			if (logger.isWarnEnabled()) {
				logger.warn("----- The object has been proxyed and method " +
						"cache interceptor can't get the target, " +
						"so the method result can't be cached which name is ------" + methodName);
			}
			
			return invocation.proceed();
		} else {
			if (invocation.getThis().getClass().isAnnotationPresent(ObjectCache.class)) {
				ObjectCache oc = invocation.getThis().getClass().getAnnotation(ObjectCache.class);
				return getResult(targetName, methodName, arguments, invocation, oc.expire());
			} else {
				
				Method[] mss = invocation.getThis().getClass().getMethods();
				Method ms = null;
				for (Method m : mss) {
					if (m.getName().equals(methodName)) {
						boolean argMatch = true;
						Class[] tmpCs = m.getParameterTypes();
						if (tmpCs.length != cs.length) {
							argMatch = false;
							continue;
						}
						for (int k = 0; k < cs.length; k++) {
							if (!cs[k].equals(tmpCs[k])) {
								argMatch = false;
								break;
							}
						}
						
						if (argMatch) {
							ms = m;
							break;
						}
					}
				}
				
				if (ms != null && ms.isAnnotationPresent(MethodCache.class)) {
					MethodCache mc = ms.getAnnotation(MethodCache.class);
					return getResult(targetName, methodName, arguments, invocation, mc.expire());
				} else {
					return invocation.proceed();
				}
			}
		}
	}
	
	private Object getResult(String targetName, String methodName, Object[] arguments,
			MethodInvocation invocation, int expire) throws Throwable {
		Object result;
		
		String cacheKey = getCacheKey(targetName, methodName, arguments);
		Element element = methodCache.get(cacheKey);
		if (element == null) {
			synchronized (this) {
				element = methodCache.get(cacheKey);
				if (element == null) {
					result = invocation.proceed();
	
					element = new Element(cacheKey, (Serializable) result);
					
					//annotation没有设expire值则使用ehcache.xml中自定义值
					if (expire > 0) {
						element.setTimeToIdle(expire);
						element.setTimeToLive(expire);
					}
					methodCache.put(element);
				}
			}
		}
		
		return element.getValue();
	}

童子们可以看到invoke方法加了一些判断(比如说类名中是否含有$Proxy),主要是防止越来越多的代理层次,如果被methodcacheinterceptor拦截到的类是一个代理类,那么ahuaxuan暂时还没有找到可以得到该代理类的目标类的方法(望知情者告之,不甚感激).

好了,好像可以告一段落了,因为现在既可以指定缓存某个类所有方法的返回结果,也可以只缓存某个类的某些方法的结果,而且还可以指定某个方法的结果被缓存多长的时间.嗯.
有童子说了:”等等,还有一个需求,我一个类中只有一个方法不需要缓存结果,其他都要缓存结果,怎么办?”
答:别烦了好吗,你就不能自己写一个@MethodNoCache吗,和@ObjectCache联合使用不就解决问题了吗.

文章最后,附上ahuaxuan的源代码,让各位见笑了.
分享到:
评论
43 楼 jef 2009-09-11  
由于service层某些方法参数取值区间不集中,没有规律,所以缓存命中率不太高,但是如果可以在dao层作缓存的话我想命中率会高些。

没太深入研究aop,不过记得aop不能解决方法嵌套后的捕获,所以aop cache应该只能为service层对象调用的第一个方法做缓存吧?那dao层的方法级cache可以做吗?
42 楼 leves 2009-08-13  
的确,如此缓存粒度以Annotation驱动控制是一种比较友好的解决方案。
对于Cache有几点想法:
1.首先,权衡中,是不是一定要缓存此粒度元素的必要,aop性能上的考虑和程序的复杂度使催生了这种懒惰的想法。
2.Cache方案的关键是Cache Key,决定了Cache的命中率。常见的Key的生成规则className+MethodName+Argument,在实际并发环境中命中率很低,这主要是因为Argument的复杂性。但在Argument比较清晰的情形下,这种生成规则的命中率会有所改观,也说明了此key生成规则的局限性。
3.根据不同的情形,设置不同的Key方案,不一刀切key生成方案,生成规则外允许自定义key。
41 楼 camelh 2009-06-02  
ahuaxuan 写道
camelh 写道

为了避免这种不必要的cache,我能不能通过配置annotation来实现呢?
可以的,可以定义哪些方法需要被缓存,methodcache,或者你再改一下,该成哪些方法不需要缓存,原理清楚了,要怎么改其实问题都不大

恩,我就是模仿你那个annotation写不需要cache. 测试后确实有效,多谢.
40 楼 ahuaxuan 2009-06-01  
camelh 写道

为了避免这种不必要的cache,我能不能通过配置annotation来实现呢?
可以的,可以定义哪些方法需要被缓存,methodcache,或者你再改一下,该成哪些方法不需要缓存,原理清楚了,要怎么改其实问题都不大
39 楼 camelh 2009-05-31  
ahuaxuan 写道
camelh 写道

楼主你好,我现在系统中用的cache方式,和你最初的版本有点像,也是用类名+方法名+参数作为参数。
如果方法的参数是对象比如数组或者VO对象,这个时候我们是不应该cache的,因为他们每次调用的key都是不一样的,这样就有个问题,比如我数组的内容都一样,但是产生的key不一样,所以每次都cache,但cache本省又不被识别。造成无用的cache。不知这个问题楼主如何解决?

遇到这种问题我们可从cache的实现原来来寻求解决方案,比如说,cache本身可以看作是一个linkedhashmap,那么如何使内容相同的数组或者VO对象在map里被认为是同一个对象呢,那么就可以想到hashcode方法和equals方法等等,那么沿着这条路下去,就可以找到解决方案了,当然如果是数组的话,确实比较麻烦,得另寻出路,比如封装等,

对于数组,你说的封装时是什么呢?我们在做cache的时候,是不能修改原来的逻辑和方法的,只能在AOP方法中做处理。为了避免这种不必要的cache,我能不能通过配置annotation来实现呢?
38 楼 ahuaxuan 2009-05-31  
camelh 写道

楼主你好,我现在系统中用的cache方式,和你最初的版本有点像,也是用类名+方法名+参数作为参数。
如果方法的参数是对象比如数组或者VO对象,这个时候我们是不应该cache的,因为他们每次调用的key都是不一样的,这样就有个问题,比如我数组的内容都一样,但是产生的key不一样,所以每次都cache,但cache本省又不被识别。造成无用的cache。不知这个问题楼主如何解决?

遇到这种问题我们可从cache的实现原来来寻求解决方案,比如说,cache本身可以看作是一个linkedhashmap,那么如何使内容相同的数组或者VO对象在map里被认为是同一个对象呢,那么就可以想到hashcode方法和equals方法等等,那么沿着这条路下去,就可以找到解决方案了,当然如果是数组的话,确实比较麻烦,得另寻出路,比如封装等,
37 楼 camelh 2009-05-30  
楼主你好,我现在系统中用的cache方式,和你最初的版本有点像,也是用类名+方法名+参数作为参数。
如果方法的参数是对象比如数组或者VO对象,这个时候我们是不应该cache的,因为他们每次调用的key都是不一样的,这样就有个问题,比如我数组的内容都一样,但是产生的key不一样,所以每次都cache,但cache本省又不被识别。造成无用的cache。不知这个问题楼主如何解决?
36 楼 daoyongyu 2008-12-23  
写的很好,谢谢楼主。
35 楼 xieke 2008-12-22  
kabbesy 写道

更好的解决方案也是有的,就是固定Key+EL表达式结合param的动态key。Cache本身也应该居于namespace的概念。不要总想着什么根据method+param自动生成key,缓存最重要的就是key。一旦你把key隐藏了,也就意味着flush的时候无法定位到具体key了
bless


能详细讲讲你的动态key吗? 什么叫 定Key+EL表达式结合param的动态key

34 楼 weina 2008-12-22  
ahuaxuan 写道
/**
*作者:张荣华
*日期:2008-11-07
**/

开门见山,一刀见血,让我们说说烦人的aop cache.

aop cache解释使用aop技术的cache,可以cache被代理对象的方法返回结果,还可以通过方法的参数值来控制缓存的粒度,看上去很美,用的人估计也颇多,好东西啊,面试的时候经常有人告诉我"我用过aop cache",看来是居家必备啊.不过居家必备的东西也得升个级什么滴啊,就想汽车一样,每年拉一次皮,照卖,还自夸是新一袋.aop cache要升级得先看看它烦人得地方.看看它烦人得地方先得知道它得用法,那么就先简单介绍一下它得用法:

常见步骤,2步
1,建立一个拦截器类,环绕增强或者后增强都可以,代码如下:
/**
 * @author ahuaxuan(aaron zhang) 代码原主是一个老外,不是我
 * @since 2008-5-13
 * @version $Id: MethodCacheInterceptor.java 814 2008-05-13 06:52:54Z aaron $
 */
@Component("methodCacheInterceptor")
@GlobalAutowired//这个是俺写的globalautowired,大家可以忽略
public class MethodCacheInterceptor implements MethodInterceptor {

	private Cache methodCache;

	public void setMethodCache(Cache methodCache) {
		this.methodCache = methodCache;
	}

	public Object invoke(MethodInvocation invocation) throws Throwable {
		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 (element == null) {
			result = invocation.proceed();

			element = new Element(cacheKey, (Serializable) result);
			methodCache.put(element);
		}
		return element.getValue();
	}

	private String getCacheKey(String targetName, String methodName,
			Object[] arguments) {
		StringBuffer sb = new StringBuffer();
		sb.append(targetName).append(".").append(methodName);
		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(arguments[i]);
			}
		}

		return sb.toString();
	}

}


这段代码很简单,就是缓存某个方法的返回结果,使用的缓存组件是ehcache,ehcache的比较详细的用法ahuaxuan在http://www.iteye.com/topic/128458这篇文章中已经有了说明.

而配置自动代理:
<!-- method cache auto proxy, add by ahuaxuan -->
     <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
         <property name="beanNames">
              <list>
                   <value>aaComponent</value>
                   <value>bbComponent</value>
              </list>
         </property>
         <property name="interceptorNames">
              <list>
                   <value>methodCacheInterceptor</value>
              </list>
         </property>
     </bean>

Over,最简单的aop cache.使用了该aop cache之后,可以缓存方法返回结果于无形,又可以根据方法参数来控制缓存粒度, 实乃居家旅行,杀人越货的必备良药

那么接下来看看这个用法有没有什么问题,相信熟悉一点的童子一眼就看出来了:”糟了,aaComponent和bbComponent所有的方法都被拦截了”.这个代码着实让我焦虑,我很焦虑.

Ok,我改,我改正则表达式还不行吗,我可以通过正则表达式让某些特定方法名的方法才被拦截处理.好啊,正统的spring用法,于是advice变成了advisor,增强变成了增强器, 但是我怎么看着就这么扭呢,难道我要缓存一个方法的结果还非得把这个方法的名字按照某个固定的格式来取, 再着,两个get方法,一个getxxx(),一个getyyy,两个之中一个需要缓存,另外一个不需要缓存(靠,真是变态),怎么办呢?正则的方式让我很烦躁,非常烦躁.

第一种方法让我焦虑,而第二种方法让我烦躁,我应该去寻找解决焦虑和烦躁的方案.

写代码需要有灵感,也需要有很强的分析能力,我们来看看我的问题是什么:
问题重新描述:不能精确的控制某个对象的某个方法需要被缓存.
思考:如何固定这个方法的标示-------------------
hardcode方法名到methodinterceptor中
hardcode缓存标示到方法上(如果该类所有方法都需要被缓存,那么hardcode缓存标示到类上)

我选第二种.那么看看实现步骤:
1.annotation类,两个:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {

}
还有一个:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCache {

}

看上去是多么无聊的两个annotation.

2修改methodinterceptor,加上判断逻辑
如果被代理的类加了ObjectCache,那么拦截这个对象所有的方法,如果没有类上没有加ObjectCache,那么判断method上有没有加methodcache,如果加了,拦截该方法,如果没有加,直接调用目标类的方法.

于是代码变成:
public Object invoke(MethodInvocation invocation) throws Throwable {
		
		String targetName = invocation.getThis().getClass().getInterfaces()[0].getName();
		String methodName = invocation.getMethod().getName();
		Object[] arguments = invocation.getArguments();
		
		if (invocation.getThis().getClass().isAnnotationPresent(ObjectCache.class)) {
			return getResult(targetName, methodName, arguments, invocation);
		} else {
			if (invocation.getMethod().isAnnotationPresent(MethodCache.class)) {
				return getResult(targetName, methodName, arguments, invocation);
			} else {
				return invocation.proceed();
			}
		}
	}

private Object getResult(String targetName, String methodName, Object[] arguments, MethodInvocation invocation) throws Throwable {
		Object result;
		
		String cacheKey = getCacheKey(targetName, methodName, arguments);
		Element element = methodCache.get(cacheKey);
		if (element == null) {
			result = invocation.proceed();

			element = new Element(cacheKey, (Serializable) result);
			methodCache.put(element);
		}
		
		return element.getValue();
	}

Ok,试试把,现在我要拦截aaservice上所有的方法,那么我的代码如下:
@ObjectCache
public class AaService  implement xxxxxx{

}


如果我要拦截bbservice上的b1方法,代码如下:
public class BbService implement xxxxxx{

	@MethodCache
	public void bb() {
		
	}
}


好了,目的达到了,我们可以任意的指定需要要拦截某个类的全部,或者部分方法了. 可是心中好像还是很闷的慌,我很慌张,非常慌张.
有人问了:都到这个份上了还慌啥张啊.
答:它tmd什么时候过期啊.我在ehcache.xml配置的可是统一的过期时间啊.ok,想到了,改,于是俺们的annotation就长成下面这个样子了:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {
	int expire() default 0;
}



@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCache {
	int expire() default 0;
}


再看看我们的进化过的methodInterceptor吧,大家可以详细比较一下下面这段和上面两端代码的异同之处
public Object invoke(MethodInvocation invocation) throws Throwable {
		
		String targetName = invocation.getThis().getClass().getInterfaces()[0].getName();
		String methodName = invocation.getMethod().getName();
		Object[] arguments = invocation.getArguments();
		Class[] cs = new Class[arguments.length];
		for (int k = 0; k < arguments.length; k++) {
			cs[k] = arguments[k].getClass();
		}
		
		if (invocation.getThis().getClass().getCanonicalName().contains("$Proxy")) {
			if (logger.isWarnEnabled()) {
				logger.warn("----- The object has been proxyed and method " +
						"cache interceptor can't get the target, " +
						"so the method result can't be cached which name is ------" + methodName);
			}
			
			return invocation.proceed();
		} else {
			if (invocation.getThis().getClass().isAnnotationPresent(ObjectCache.class)) {
				ObjectCache oc = invocation.getThis().getClass().getAnnotation(ObjectCache.class);
				return getResult(targetName, methodName, arguments, invocation, oc.expire());
			} else {
				
				Method[] mss = invocation.getThis().getClass().getMethods();
				Method ms = null;
				for (Method m : mss) {
					if (m.getName().equals(methodName)) {
						boolean argMatch = true;
						Class[] tmpCs = m.getParameterTypes();
						if (tmpCs.length != cs.length) {
							argMatch = false;
							continue;
						}
						for (int k = 0; k < cs.length; k++) {
							if (!cs[k].equals(tmpCs[k])) {
								argMatch = false;
								break;
							}
						}
						
						if (argMatch) {
							ms = m;
							break;
						}
					}
				}
				
				if (ms != null && ms.isAnnotationPresent(MethodCache.class)) {
					MethodCache mc = ms.getAnnotation(MethodCache.class);
					return getResult(targetName, methodName, arguments, invocation, mc.expire());
				} else {
					return invocation.proceed();
				}
			}
		}
	}
	
	private Object getResult(String targetName, String methodName, Object[] arguments,
			MethodInvocation invocation, int expire) throws Throwable {
		Object result;
		
		String cacheKey = getCacheKey(targetName, methodName, arguments);
		Element element = methodCache.get(cacheKey);
		if (element == null) {
			synchronized (this) {
				element = methodCache.get(cacheKey);
				if (element == null) {
					result = invocation.proceed();
	
					element = new Element(cacheKey, (Serializable) result);
					
					//annotation没有设expire值则使用ehcache.xml中自定义值
					if (expire > 0) {
						element.setTimeToIdle(expire);
						element.setTimeToLive(expire);
					}
					methodCache.put(element);
				}
			}
		}
		
		return element.getValue();
	}

童子们可以看到invoke方法加了一些判断(比如说类名中是否含有$Proxy),主要是防止越来越多的代理层次,如果被methodcacheinterceptor拦截到的类是一个代理类,那么ahuaxuan暂时还没有找到可以得到该代理类的目标类的方法(望知情者告之,不甚感激).

好了,好像可以告一段落了,因为现在既可以指定缓存某个类所有方法的返回结果,也可以只缓存某个类的某些方法的结果,而且还可以指定某个方法的结果被缓存多长的时间.嗯.
有童子说了:”等等,还有一个需求,我一个类中只有一个方法不需要缓存结果,其他都要缓存结果,怎么办?”
答:别烦了好吗,你就不能自己写一个@MethodNoCache吗,和@ObjectCache联合使用不就解决问题了吗.

文章最后,附上ahuaxuan的源代码,让各位见笑了.

33 楼 wangxuliangboy 2008-12-18  
AOP CACHE  命中率会很低吧...每次调用参数不同的话,,就没什么用啦...

代理类的目标类的方法:
每一个代理出来的类.命名都是有规则的..
timeout.Test$$EnhancerByCGLIB$$ab0d57f7
这是CGLIB代理出来的话...$$符号前面是完整类路径....


代理类的目标类的方法,不知道你这句话是不是上面的意思
32 楼 wcleye 2008-12-18  
ispring 写道
为什么不把两个Annotation合并在一起写呢,比如:
@Target(ElementType.METHOD | ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface MethodCache {  
 
}


大哥,你有没看代码,一个拦截整个类,一个拦截指定方法....
31 楼 WiseNeuron 2008-12-03  
学习了:)
30 楼 ispring 2008-12-03  
为什么不把两个Annotation合并在一起写呢,比如:
@Target(ElementType.METHOD | ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface MethodCache {  
 
}
29 楼 ahuaxuan 2008-11-26  
kabbesy 写道
缓存这么用是很显然的,如果你能原创于此,也是不错了。
但不过这样的设计很早很早就有了,参见06年网上关于aop cache的文章,以及更早的英文文章。具体实现06年不到开始,也有了 spring modules cache。跟你的实现差不太多,只是那个是兼容1.4的。

aopcache这个思想确实不是我原创,其实我在文章一开始就注明了,它的出现也不是06年,而是04年就有了,但是思想是思想,实现是实现,我至少我到现在还没有发现有谁的aopcache实现和我一样,说到这里我不禁怀疑你没有看这篇文章,我用的annotation来实现aop cache的,同时也可以通过annotation来指定缓存的过期时间,你说我不是原创,麻烦你把别人的东西拿出来看看可以吗(我们可以在代码上比对一下).你可以看看我的其他文章,哪篇不是原创,非原创的东西我不会发在这里的

而且缓存这种东西不是一篇文章可以写完的,如果让我系统的写下来的话,估计会超过5篇,所以本文只讨论aop cache实现方法,与aop cache的使用场景,它只是一把锤子.如果你有比较好的关于aop cache的实现,望不吝赐教




28 楼 kabbesy 2008-11-26  
缓存这么用是很显然的,如果你能原创于此,也是不错了。
但不过这样的设计很早很早就有了,参见06年网上关于aop cache的文章,以及更早的英文文章。具体实现06年不到开始,也有了 spring modules cache。跟你的实现差不太多,只是那个是兼容1.4的。

就具体应用而言,这种方案太weak了,除非是业务场景非常简单的情况,否则你的cache将不断面临着invalid,然后flush by cache,而不是remove by key。

spring modules cache项目也就是在这个问题上找不到解决方案,止步不前而迟迟未能推出0。10版本。

更好的解决方案也是有的,就是固定Key+EL表达式结合param的动态key。Cache本身也应该居于namespace的概念。不要总想着什么根据method+param自动生成key,缓存最重要的就是key。一旦你把key隐藏了,也就意味着flush的时候无法定位到具体key了

annotation作为AOP pointcut是个很好的趋势,除了spring2推出的@Transactional以外,还应该有很多应用。最起码,非常适合缓存这种需要精确粒度(缓存决不能搞什么get*的pointcut,那就歇菜了),而且业务逻辑又很简单的切面操作了。

bless
27 楼 ahuaxuan 2008-11-19  
sorphi 写道


为什么大多数程序员不喜欢用key的方式?我怎么觉得这个比默认的key策略(类+方法名+参数)灵活、实用呢?


问了一下身边的同事,都比较认同使用class+method+parameter的方式.他们的理由是这个key其实也用不到自己去控制,当然我和他们的观点是一样的,不过由于我和同事之间在技术上的观点有一定的同化性,所以你也可以了解一下身边同事的看法,这样才比较客观
26 楼 sorphi 2008-11-19  
ahuaxuan 写道

sorphi 写道
俺只就annotation来提的建议:

@Cache(key="user_{1}_{2}",group="user")
public User findUser(param1,param2);



@Flush(group="user")
public void updateUser();

你说的方法确实是一种方案,也许可以这样,如果没有key的情况就使用类+方法名+参数,如果有key的情况,则用key.

不过我还是没有想到这样做到底有什么特别的优点,因为如果向上面这样实现,大多数程序员估计不喜欢用key的方式


为什么大多数程序员不喜欢用key的方式?我怎么觉得这个比默认的key策略(类+方法名+参数)灵活、实用呢?
25 楼 ahuaxuan 2008-11-19  
Feiing 写道
inteceptor 内部不应该关注 Pointcut, 应该用 AnnotationMethodMatcher 控制切入点

哦,理解了,3x

用在methodcache上没有问题,如果用到objectcache上好像不行

修改:看来要自己写AnnotationClassMatcher才行了


sorphi 写道
俺只就annotation来提的建议:

@Cache(key="user_{1}_{2}",group="user")
public User findUser(param1,param2);



@Flush(group="user")
public void updateUser();

你说的方法确实是一种方案,也许可以这样,如果没有key的情况就使用类+方法名+参数,如果有key的情况,则用key.

不过我还是没有想到这样做到底有什么特别的优点,因为如果向上面这样实现,大多数程序员估计不喜欢用key的方式
24 楼 Feiing 2008-11-19  
inteceptor 内部不应该关注 Pointcut, 应该用 AnnotationMethodMatcher 控制切入点

相关推荐

    SpringBoot AOP各种注解、自定义注解、鉴权使用案例(免费下载)

    public Object cache(ProceedingJoinPoint pjp, Cacheable cacheable) throws Throwable { // 缓存逻辑... } } ``` 最后,我们讨论一下鉴权。在SpringBoot中,权限验证可以通过AOP来实现,例如使用Spring ...

    SpringAOP结合ehCache实现简单缓存实例

    接下来,我们将讨论如何整合Spring AOP和EhCache来创建一个简单的缓存实例: 1. **引入依赖**:首先,你需要在项目的pom.xml文件中添加Spring AOP和EhCache的相关依赖。确保引入最新版本的Spring Framework和...

    SSM与memcached整合项目Spring Cache

    下面我们将详细讨论这个整合过程中的关键知识点。 首先,让我们了解Spring Cache。Spring Cache提供了一个统一的API,用于在应用中添加缓存功能,无论你选择哪种缓存提供商。它通过AOP(面向切面编程)来实现,可以...

    给DAL层加上Cache(张宁).pdf

    在本文中,我们将讨论如何给DAL层加上Cache,以提高系统的性能和减少数据库访问次数。我们将通过一个实例来说明如何实现缓存机制,并讨论使用AOP(Aspect-Oriented Programming)框架来解耦合数据库访问和缓存逻辑。...

    AutoFac+缓存+Redis

    接下来,我们讨论如何将 Redis 集成到 AutoFac 中。为了实现缓存功能,我们需要一个支持 Redis 的缓存提供者。可以使用 StackExchange.Redis 这个库,它是官方推荐的 .NET 客户端,提供了丰富的 API 来操作 Redis ...

    SpringBoot 仿牛客网讨论社区项目

    在实现这些功能时,开发者会运用到Spring Boot的各种特性,如AOP(面向切面编程)进行日志记录,Spring Data JPA或MyBatis进行数据操作,Spring Cache实现缓存,以及Spring Boot Actuator监控应用健康状态等。...

    spring-cache-redis

    对于Spring Cache,我们需要添加`spring-context`、`spring-aop`和`spring-expression`等核心模块,以及`spring-data-redis`模块来支持Redis。同时,还需要引入Redis客户端Jedis或Lettuce的依赖。 ```xml &lt;!-- ...

    Java缓存讨论.pdf

    TreeCache模块提供树形结构的缓存,而TreeCacheAOP则利用面向切面编程(AOP)对POJO进行动态管理。 OSCache是由OpenSymphony开发的高性能J2EE缓存框架,它允许开发者在JSP页面内直接实现内存缓冲。OSCache的特点包括...

    jbosscache manual

    - **事务处理**:讨论 PojoCache 如何支持事务,包括事务的提交与回滚机制。 #### 3. 配置 为了使用 JBoss Cache,首先需要下载 JBoss Cache 3.x 发行版。可以从官方下载页面 ...

    spring支持ehcache

    【描述】"使用spring对缓存ehcache进行管理"表明我们将讨论如何通过Spring框架来配置和控制Ehcache的使用。Spring 提供了 Cache Abstraction,使得集成各种缓存系统变得简单,其中包括Ehcache。 首先,要使用Spring...

    Spring和emcache整合demo

    我们将讨论以下几个核心知识点: 1. **Spring框架**:Spring是一个开源的Java平台,它简化了Java企业级应用程序的开发。它提供了依赖注入(DI)、面向切面编程(AOP)以及一系列的模块,如数据访问、Web MVC、测试...

    spring+springmvc

    10. **优化**:性能优化包括使用缓存技术(如Spring Cache或Redis),调整数据库连接池配置,以及利用Spring AOP进行事务管理优化等。 总之,SSM是一个强大的Java Web开发组合,它提供了完整的功能,从控制层到持久...

    Spring-MYBatis企业应用实战-有详细目录

    例如,利用Spring的AOP特性实现全局事务管理,或者通过MyBatis的Cache接口自定义缓存机制。此外,对于性能优化,如批处理SQL、减少数据库连接开销等方面也会有所涉及。 最后,可能还会介绍如何将Spring-MYBatis应用...

    hands-high-performance-spring-5

    本内容将围绕Spring 5的关键特性和性能优化策略展开讨论。 1. **Spring 5新特性** - **WebFlux**: 引入了响应式编程模型,支持非阻塞I/O,提高服务器处理高并发的能力。 - **WebSocket支持增强**: 提供更好的实时...

    java面试宝典

    24. **Cache技术**:Ehcache和Memcached的原理及使用。 25. **SQL优化**:涉及查询优化、索引的原理和使用。 26. **Oracle数据库**:理解rownum和rowid的区别,以及分页查询的方法。 27. **执行计划分析**:学习...

    Java_Web整合开发王者归来_11

    这部分可能讨论了如何通过缓存技术(如Spring Cache或Redis)、负载均衡、数据库优化、代码优化等手段来提升系统的响应速度和并发能力。 另外,文件名中的"00011.pdf"表明这可能是系列教程的第11章或者第11部分,...

    Hibernate程序高手秘笈.part01-03.rar

    10. 扩展与高级主题:涉及Hibernate的事件监听、拦截器、动态模型、批量操作和CGLIB/AOP集成等内容,帮助读者掌握更高级的Hibernate技巧。 11. 实战案例:书中可能会提供一些实际项目案例,让读者将所学知识应用于...

    Java工具包Hutool Wiki PDF版

    1. hutool-aop:封装了JDK动态代理,提供了非依赖于IOC容器的面向切面编程(AOP)功能。 2. hutool-bloomFilter:实现了布隆过滤器,用于快速地判断元素是否存在集合中。 3. hutool-cache:提供了简单易用的缓存功能...

    (毕业设计)基于SSM的校园论坛系统的设计与实现.zip

    2. 论坛板块:设计不同的讨论区,用户可以根据兴趣选择参与讨论。可以使用Spring Data JPA来操作数据库,方便查询和更新板块信息。 3. 主题与帖子:用户可以发布新主题或在已有的主题下发表回复。需要考虑帖子的...

    java游戏论坛 毕业设计

    它提供了一个AOP(面向切面编程)框架,允许在不修改代码的情况下进行功能增强。此外,Spring还支持JDBC抽象层和事务管理,降低了数据库操作的复杂性。 2. **Struts框架**:Struts作为MVC(模型-视图-控制器)架构...

Global site tag (gtag.js) - Google Analytics