锁定老帖子 主题:殊途同归
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-08-11
我先按OO TDD步骤来进行: 1、首先简化Adrian Colyer原来的TestCase,在DataProvider的前后两次expensiveOperation操作,期望节省0.5s 。 public class CachingTest extends TestCase { private DataProvider provider; public void testExpensiveOperationCache(); { long start100 = System.currentTimeMillis();; int op100 = provider.expensiveOperation(100);; long stop100 = System.currentTimeMillis();; long start100v2 = System.currentTimeMillis();; int op100v2 = provider.expensiveOperation(100);; long stop100v2 = System.currentTimeMillis();; long expectedSpeedUp = 500; // expect at least 0.5s quicker with cache assertTrue("caching speeds up return (100);", ((stop100 - start100); - (stop100v2 - start100v2);); >= expectedSpeedUp);; assertEquals("cache returns correct value(100);",op100,op100v2);; } /* * @see TestCase#setUp(); */ protected void setUp(); throws Exception { super.setUp();; provider = new DataProvider(100);; } } 2、接着就是让程序能编译通过,并且使TestCase red。 public class DataProvider { private int multiplicationFactor = 0; public DataProvider(int seed); { multiplicationFactor = seed; } /** * expensiveOperation is a true function (it always * returns the same output value for a given input * value);, but takes a long time to compute the * answer. */ public int expensiveOperation(int x); { try { Thread.sleep(1000);; } catch (InterruptedException ex); {} return x * multiplicationFactor; } } 3、此时,就应该考虑如何让TestCase green。 public class DataProvider { [color=red]private Map result;[/color] private int multiplicationFactor = 0; public DataProvider(int seed); { multiplicationFactor = seed; result = new HashMap();; } /** * expensiveOperation is a true function (it always * returns the same output value for a given input * value);, but takes a long time to compute the * answer. */ public int expensiveOperation(int x); { if (result.containsKey(new Integer(x););); { return Integer.parseInt(result.get(new Integer(x);););; } else { try { Thread.sleep(1000);; } catch (InterruptedException ex); {} result.put(new Integer(x);, new Integer( x * multiplicationFactor););; return x * multiplicationFactor; } } } 4、OK,测试通过了。接下去我们用相同的方法来处理DataProvider1,DataProvider2... public class DataProvider1 { private Map result; private int multiplicationFactor = 0; public DataProvider1(int seed); { multiplicationFactor = seed; result = new HashMap();; } /** * expensiveOperation is a true function (it always * returns the same output value for a given input * value);, but takes a long time to compute the * answer. */ public int expensiveOperation(int x); { if (result.containsKey(new Integer(x););); { return Integer.parseInt(result.get(new Integer(x);););; } else { try { Thread.sleep(1000);; } catch (InterruptedException ex); {} result.put(new Integer(x);, new Integer( x + multiplicationFactor););; return x + multiplicationFactor; } } } 5、闻到代码味道了吧,开始重构,把相同的代码抽取出来,创建一个Abstract Class。 public class CachingTest extends TestCase { private AbstractDataProvider provider; public void testCachedOperation(); { long start100 = System.currentTimeMillis();; int op100 = provider.cachedOperation((100);; long stop100 = System.currentTimeMillis();; long start100v2 = System.currentTimeMillis();; int op100v2 = provider.cachedOperation((100);; long stop100v2 = System.currentTimeMillis();; long expectedSpeedUp = 500; // expect at least 0.5s quicker with cache assertTrue("caching speeds up return (100);", ((stop100 - start100); - (stop100v2 - start100v2);); >= expectedSpeedUp);; assertEquals("cache returns correct value(100);",op100,op100v2);; } /* * @see TestCase#setUp(); */ protected void setUp(); throws Exception { super.setUp();; provider = new DataProvider(100);; } } public abstract class AbstractDataProvider { private Map result; private int multiplicationFactor = 0; public AbstractDataProvider(int seed); { multiplicationFactor = seed; result = new HashMap();; } public int cachedOperation(int x); { int ret = 0; Integer key = new Integer(x);; if (result.containsKey(key);); { Integer val = (Integer); result.get(key);; ret = val.intValue();; } else { ret = expensiveOperation(x);; result.put(key,new Integer(ret););; } return ret; } /** * expensiveOperation is a true function (it always * returns the same output value for a given input * value);, but takes a long time to compute the * answer. */ protected abstract int expensiveOperation(int x);; } public class DataProvider extends AbstractDataProvider { public DataProvider(int seed); { super(seed);; } /** * expensiveOperation is a true function (it always * returns the same output value for a given input * value);, but takes a long time to compute the * answer. */ protected int expensiveOperation(int x); { try { Thread.sleep(1000);; } catch (InterruptedException ex); {} return x * multiplicationFactor; } } 6、All Right,终于实现一个简单的Caching机制。瞧瞧我们都有哪些改变: 其一,抽取了一个新的抽象类,子类都需要去扩展这个抽象类; 其二,增加了一个cachedOperation方法; 其三,expensiveOperation方法的访问方式修改为protected 下面,我们再试试AOP TDD,这个例子使用AspectJ: 1、首先简化Adrian Colyer原来的TestCase,在DataProvider的前后两次expensiveOperation操作,期望节省0.5s 。 2、接着就是让程序能编译通过,并且使TestCase red。 前两个步骤与原来的是完全相同的,第三步就有些不同了。 3、增加一个aspect,并让TestCase green。 public aspect BogBasicHardWiredCache { private Map operationCache = new HashMap();; pointcut expensiveOperation(int x); : execution(* DataProvider.expensiveOperation(int);); && args(x);; /** * caching for expensive operation */ int around(int x); : expensiveOperation(x); { int ret = 0; Integer key = new Integer(x);; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(x);; operationCache.put(key,new Integer(ret););; } return ret; } } 4、啊,测试也通过了。接下去我们再来处理DataProvider1,DataProvider2... public aspect BogBasicHardWiredCache1 { private Map operationCache = new HashMap();; pointcut expensiveOperation(int x); : execution(* DataProvider1.expensiveOperation(int);); && args(x);; /** * caching for expensive operation */ int around(int x); : expensiveOperation(x); { int ret = 0; Integer key = new Integer(x);; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(x);; operationCache.put(key,new Integer(ret););; } return ret; } } 5、几个aspect差不多嘛,再重构。 public aspect BogBasicHardWiredCache pertarget(expensiveOperation(int);); { private Map operationCache = new HashMap();; pointcut expensiveOperation(int x); : execution(* expensiveOperation(int);); && args(x);; /** * caching for expensive operation */ int around(int x); : expensiveOperation(x); { int ret = 0; Integer key = new Integer(x);; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(x);; operationCache.put(key,new Integer(ret););; } return ret; } } 6、收工,我们做了些什么呢?仅仅只是创建了一个aspect。 简单的例子可能给我们带来一些启示,但是走哪条道需要你自己选择,不是吗? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2004-08-11
唉,看来你是彻底没有看明白偶在Log, Cache, AOP的常用谎言? (嗡嗡作响的AOP系列之二)里面骂的东西: Cache不是这么简单的, AOPer不要老是举这些玩具例子了, 何况Adrian的这个例子里, CacheAspect写比偶在那篇文章里面抄的要烂N倍, 更本不能重用, 代码也写得好糊烂, AOPer鼓吹的无侵入性也完全没有体现出来!! 按照前面某位大哥说的"现在还在捣鼓基础的log??",完全可以骂他"现在还在捣鼓这种骗小孩cache??"了,
不过看在你用代码说话的份上, 偶还是乐意和你唐僧一下, 然后让你一步一步地掉入偶的陷阱, 来看看偶的实现, 首先改写成偶喜欢的小可爱: 接口正切, public interface DataProvider { public int expensiveOperation(int x);; public int cachedOperation(int x);; public void setFactor(int factor);; public int getFactor();; public CacheManager getCacheManager();; public void setCacheManager(CacheManager cacheManager);; } public interface CacheManager { public Object get(Object key);; public void put(Object key, Object value);; public void clear(Object key);; public void clearAll();; } 实现: public abstract class AbstractDataProvider implements DataProvider { private int factor; private CacheManager cacheManager; public int cachedOperation(int x); { Integer key = new Integer(x);; Integer result = (Integer); cacheManager.get(key);; if (result == null); { result = new Integer(expensiveOperation(x););; cacheManager.put(key, result);; } return result.intValue();; } public void setFactor(int factor); { this.factor = factor; } public int getFactor(); { return factor; } public CacheManager getCacheManager(); { return cacheManager; } public void setCacheManager(CacheManager cacheManager); { this.cacheManager = cacheManager; } } public class DataProviderOne extends AbstractDataProvider { public int expensiveOperation(int x); { try { Thread.sleep(1000);; } catch (InterruptedException e); { } return x * getFactor();; } } public class CacheManagerImpl implements CacheManager{ private HashMap cache = new HashMap();; public Object get(Object key); { return cache.get(key);; } public void put(Object key, Object value); { cache.put(key, value);; } public void clear(Object key); { cache.remove(key);; } public void clearAll(); { cache.clear();; } } 稍微修改一下测试代码: public class DataProviderTest extends TestCase { private DataProvider provider; public void testCachedOperation(); { long start100 = System.currentTimeMillis();; int op100 = provider.cachedOperation(100);; long stop100 = System.currentTimeMillis();; long start100v2 = System.currentTimeMillis();; int op100v2 = provider.cachedOperation(100);; long stop100v2 = System.currentTimeMillis();; long expectedSpeedUp = 500; // expect at least 0.5s quicker with cache assertTrue("caching speeds up return (100);", ((stop100 - start100); - (stop100v2 - start100v2);); >= expectedSpeedUp);; assertEquals("cache returns correct value(100);",op100,op100v2);; } protected void setUp(); throws Exception { provider = new DataProviderOne();; CacheManagerImpl cm = new CacheManagerImpl();; provider.setCacheManager(cm);; provider.setFactor(100);; } } 绿油油的通过了 偶的问题来了, 首先Cache不是光Cache了东西就完事的, 记得擦屁股, 偶多加一个测试用例: public void testClearCache(); { int op1 = provider.cachedOperation(1);; int op1v2 = provider.cachedOperation(1);; assertTrue(100 == op1);; assertTrue(100 == op1v2);; provider.setFactor(200);; int op1v3 = provider.cachedOperation(1);; assertTrue(200 == op1v3);; } 先修改你的AspectJ让它通过看看? (不过在修改以前, 先抄抄偶的那个CacheAspect, 因为后面的陷阱都和CacheAspect重用有关呢), 偶再来修改代码, 然后给你挖第2个, 第3个陷阱....... |
|
返回顶楼 | |
发表时间:2004-08-11
既然都已经摆下陷阱,那就尽我所能吧!
下面这个aspect就可以让你的测试通过了,不过你的testcase code有点问题,我姑且认为assertTrue(200 == op1v2)中的op1v2应该是op1v3吧。需要说明的一点是:我把BogBasicHardWiredCache改名为SimpleCache。 public aspect SimpleCache { private Map operationCache = new HashMap();; pointcut expensiveOperation(int x); : execution(* DataProvider.expensiveOperation(int);); && args(x);; before(DataProvider dp, int factor);: target(dp); && args(factor); && call(void setFactor(int);); { int oldValue = dp.getFactor();; if (0 != oldValue && oldValue != factor); { operationCache.clear();; return; } } /** * caching for expensive operation */ int around(int x); : expensiveOperation(x); { int ret = 0; Integer key = new Integer(x);; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(x);; operationCache.put(key,new Integer(ret););; } return ret; } } |
|
返回顶楼 | |
发表时间:2004-08-11
代码擂台?噢,我喜欢,搬个凳子来坐……
晚点来掺和一下,不过,我可不管什么Aspect不Aspect的,看着爽,用着舒心就行。 |
|
返回顶楼 | |
发表时间:2004-08-11
后山 写道 下面这个aspect就可以让你的测试通过了,不过你的testcase code有点问题,我姑且认为assertTrue(200 == op1v2)中的op1v2应该是op1v3吧。需要说明的一点是:我把BogBasicHardWiredCache改名为SimpleCache。
-_-! 汗, 偶连test code也错......, 该打...... OK, 先来看看偶的实现是怎么改的, 只用加一句话: public void setFactor(int factor); { if(this.factor != factor); cacheManager.clearAll();; this.factor = factor; } 但是AOPer可以说: 虽然我们清除Cache的代码看起来比较多一些, 但是如果影响到Cache内容的触发点不只setFactor一处的话, 我们只要修改ponitcut的匹配就好了, 而你还得在N处地方加入clear cache的代码, 慢着, 实际应用的cache不是一次清除干净, 我们需要的是聪明的cache, 只要清除对应的Cache, 不应该一股脑清除掉. 在看smart cache clear以前, 先扔出第2个问题:CacheAspect如何做到聪明的Cache Key重用? DataProvider多加了一个方法: cachedOperationTwo public void testCachedOperationTwo(); { long start1_2 = System.currentTimeMillis();; int op1_2 = provider.cachedOperationTwo(1, 2);; long stop1_2 = System.currentTimeMillis();; long start1_2v2 = System.currentTimeMillis();; int op1_2v2 = provider.cachedOperationTwo(2, 1);; long stop1_2v2 = System.currentTimeMillis();; long expectedSpeedUp = 500; // expect at least 0.5s quicker with cache assertTrue("caching speeds up return", ((stop1_2 - start1_2); - (stop1_2v2 - start1_2v2);); >= expectedSpeedUp);; assertTrue(200 == op1_2);; assertTrue(200 == op1_2v2);; } 这次先来给出偶的实现: AbstractDataProvider.java public int cachedOperationTwo(int x, int y); { String key = "OP2" + x * y; Integer result = (Integer); cacheManager.get(key);; if (result == null); { result = new Integer(expensiveOperationTwo(x, y););; cacheManager.put(key, result);; } return result.intValue();; } DataProviderOne.java public int expensiveOperationTwo(int x, int y); { try { Thread.sleep(1000);; } catch (InterruptedException e); { } return x * y * getFactor();; } 但是由于AspectJ对于JoinPoint的上下文不了解, AOP号称的code re-use根本无法实现这种聪明的Cache Key重用, 看看你能如何写出简洁的实现? 第3个问题: smart cache clear, 等你解决了上面的这个陷阱, 偶再来说. |
|
返回顶楼 | |
发表时间:2004-08-12
后山,你是想展示cache还是想展示aspect?aspect的特点是不是分离的关注点与具体的属主类型无关?你的测试代码里关于横切的那部分我没看出来无关性。Readonly的测试代码可是跟具体的类密切相关的。
|
|
返回顶楼 | |
发表时间:2004-08-12
感觉很多介绍aop的文章拿来做对比的oo都是已经受到批判的用实现继承和类层次来组织代码的方法。而对现在基于接口正交的方法很少提及。
这就很难让人心服了。打落水狗谁不会呢? 不过这个cache确实可以引出很有趣的例子的。 read-only给的例子正是当cache和具体应用逻辑比较紧密地耦合的情况。对此,aop也许至少可以说:我们面对的不是这种紧耦合的情况,在情况没有这么复杂的更简单的情况,aop确实可以让代码更漂亮。 打击对手的软肋固然有效,但是也许没有强攻对手的长处更有说服力(毕竟,谁没有弱点呢?) 所以,我选择不打乱aop的脚步,与狼共舞,看看效果如何。 对贴主给的这个例子,太简单了,接口正交完全可以处理的嘛。 比如, 先假设我们要处理的是这样一个业务逻辑(btw,我很反感对business logic省略接口,上来就做class。) interface Business{ String f(String s);; } 然后,不管事实上有BusinessImpl1, BusinessImpl2,...多少种不同的实现,我们只对Business接口实现cache的decorator. final class CachedBusiness implements Business{ private final Map cache = new HashMap();; private final Business real; public String f(String s);{ final Object ret = cache.get(s);; if(s==null);{ final String r = real.f(s);; cache.put(s, r);; return r; } else return (String);ret; } } 我省略了构造函数等细节。(题外话,有人可能对final有看法,呵呵,我个人的喜好,是能final就final,没有足够的理由,绝对不允许别人继承我。要customize?有接口给你,decorator, brige, adapter,随你便。) 对这样一个decorator,如果你想cache某一个Business,就这样做: Business createBusiness(...);{ return new CachedBusiness(new BusinessImpl1(...););; } 完全对外界透明。 如此,对aop给出的最简单的例子,我看不出它对这种接口正交的方法有什么优势。 然后,让我们把问题稍微复杂化一点:加入setFactor(),也就是说,有一个函数要清空cache。 如read-only所作,接口变成: interface Business{ String f(String s);; void setFactor(int i);; } 我们的cache变成: final class CachedBusiness{ private final Map cache = new HashMap();; private final Business real; public String f(String s);{ final Object ret = cache.get(s);; if(s==null);{ final String r = real.f(s);; cache.put(s, r);; return r; } else return (String);ret; } void setFactor(int i);{ cache.clear();; real.setFactor(i);; } } 当然,我们可以更聪明点,记住上次的factor,然后只有当factor不同的时候才clear,这些都是小节了。 so far,仍然是没有看出aop对接口正交的优势。 好,我们再复杂一点:有不只一个函数要求cache。自然这些函数一般都要各自有自己的cache。 接口变成: interface Business{ String f1(String s);; String f2(String s1, int s2);; void setFactor(int);;//这个函数影响f1的cache。 void invalidate(String s);;//这个函数影响f1和f2的cache。 void g();;//这个函数只应先f2的cache。 } 啊呀,一下子问题复杂了。 我们面对的难点有: 1。可以只cache f1, 只cache f2,也可以两者都cache 2。f2有两个参数,如何决定cache的key。 3。对setFactor, invalidate(), g()这几个函数要有不同处理。 对1, 最好的办法是对cache f1, cache f2分别做一个类,然后就可以进行组合。这样,即使有10个可能要cache的函数,我们也不过做十个不同的类,然后用户用这个十个decorator自己组合就是了,比如: new BusinessCache1( new BusinessCache2( new BusinessCache10( new BusinessImpl2(); ); ); ); 对二,这绝对是跟f2的语义直接相关的,一个generic的cache是不可能知道如何组成key的。 对三,哎,麻烦,在BusinessCache1里面,我们要处理setFactor和invalidate,在BusinessCache2里面,则要处理invalidate和g()。 假如关系更加复杂一些,比如,有十个要cache的函数,有二十个可能要清除不同cache的其他函数,老天,这个组合可是很庞大的。 下面在我继续之前,让我先写一点代码,示意一下问题的麻烦程度: interface Business{ int f1(int a);; String f2(String b);; char f3(int a, String b, int c);; void invalidate1();; int invalidate12(int x);; } 这个接口有f1, f2, f3三个要cache的费时操作。有invalidate1, invalidate12两个要清cache的函数。 下面对cache f1, cache f2分别写一个类: final class CachedF1Business implements Business{ private final Map cache = new HashMap();; private final Business real; public int f1(int a);{ final Integer k = new Integer(a);; final Object ret = cache.get(k);; if(ret==null);{ final int r = real.f1(a);; cache.put(k, new Integer(r););; return r; } else{ return ((Integer);ret);.intValue();; } } public String f2(String b);{return real.f2(b);;} public char f3(int a, String b, int c);{ return real.f3(a,b,c);; } public void invalidate1();{ cache.clear();; real.invalidate1();; } public int invalidate12(int x);{ cache.clear();; return real.invalidate2(x);; } public CachedF1Business(Business r);{ this.real = r; } } final class CachedF2Business implements Business{ private final Map cache = new HashMap();; private final Business real; public int f1(int a);{ return real.f1(a);; } public String f2(String b);{ final String k = b; final Object ret = cache.get(b);; if(ret==null);{ final String r = real.f2(b);; cache.put(k, r);; return r; } else{ return (String);ret; } } public char f3(int a, String b, int c);{ return real.f3(a,b,c);; } public void invalidate1();{ real.invalidate1();; } public int invalidate12(int x);{ cache.clear();; return real.invalidate2(x);; } public CachedF1Business(Business r);{ this.real = r; } } 哎,闻到坏味道了。 cache的逻辑很相似,但是被重复在Cache1和Cache2两个类中。 清cache的操作也很相似,也是分别散落到了不同的地方。 如果我们总结一下,给cache写一下伪码,应该是这样: if method is cached key = create key from arguments v = cache.find(key); if(v==null); r = call the real expensive operation put r in cache return r else return v else if method needs to clear cache clear cache call the real operation and return the value. else delegate to the real operation 对这样一个同样的逻辑在不同的地方用相似(注意,并非相同)的代码重复总是不舒服。 我想,这大概就是aop所要解决的一个具体例子吧。 在c++里面,我是无计可施了,即使用尽meta-programming, traits,我相信也是徒劳。也许可以找到些没有希望中的强大(但是扔然极为复杂)的heuristic的解决方案,但是,没有完美的解决办法。 同样的,用静态类型的java我相信也是达不到这个要求的。 幸好,java有dynamic proxy。它虽然损害了类型安全,有点overkill,但是处理这种复杂问题正好是强项。 让我们看看dynamic proxy如何解决问题: final class Caching implements InvocationHandler{ private final Map cache = new HashMap();; private final Object real; private final MethodPredicate target; private final KeyGen gen; private final MethodPredicate filter; private void invalidate(Method mtd);{ if(filter.eval(mtd););{ cache.clear();; } } public Object invoke(Object proxy, Method mtd, Object[] args);{ invalidate(mtd);; if(target.eval(mtd););{ final Object key = gen.generate(mtd,args);; final Object ret = cache.get(key);; if(ret==null);{ final Object r = mtd.invoke(real, args);; if(r!=null);{ cache.put(key, r);; return r; } } } else{ return mtd.invoke(real, args);; } } } 呵呵,和伪码非常象。 MethodPredicate, KeyGen都是接口。其中MethodPredicate负责选择要cache和要清空cache的方法,KeyGen用来生成cache key。 具体实现我就不写了,相信谁都写得出来。 这样,是不是也和aop的实现差不多呢? 当然,也许有的人会说:你这已经是aop了。 我的回答是:是吗?太好了。那么,难道aop就是interceptor么? |
|
返回顶楼 | |
发表时间:2004-08-12
怎么觉得有点糊里糊涂的。代码之前请写个简要的介绍和说明。
另外,对函数的cache?对类方法的cache有什么意义?也许某些情况下有用,不过这种说法放到这个例子里我也没看出来到底哪里在cache你的函数啊。 我希望能够更清晰简单的看懂代码。 |
|
返回顶楼 | |
发表时间:2004-08-12
ajoo, 你, 你, 竟然把偶准备好的陷阱一下子都扔出来了......,
这种做法和AOP相比, CachedBusiness仅仅是多写一堆delegate代码给Business, 即便使用dynamic proxy的代码也很清晰, 而AOP鼓吹的一堆buzzword却是偶这样愚蠢的脑袋所不能容忍的...... 不说了, 等看AOP怎么做吧, 或许没有深入了解AOP的偶们都是愚蠢的...... |
|
返回顶楼 | |
发表时间:2004-08-12
Readonly 写道 先扔出第2个问题:CacheAspect如何做到聪明的Cache Key重用? 重用还是有可能的,还是按TDD来实现。不过要说明一点是:我并没有任何否定OO的言论。 1、在aspect中加入一个新的pointcut & advice。 /** * caching for expensive operation two */ pointcut expensiveOperationTwo(int x, int y); : execution(* DataProvider.expensiveOperationTwo(int, int);); && args(x, y);; int around(int x, int y); : expensiveOperationTwo(x, y); { int ret = 0; String key = "OP2" + x * y; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(x, y);; operationCache.put(key,new Integer(ret););; } return ret; } 2、这个pointcut & advice与原来的很相似,主要的不同在于:参数数目和key生成策略。为了重用,必须要解决这两个问题。首先,让我们来着手解决参数数目,修改上面的pointcut & advice。 int around(DataProvider dp);: execution(* DataProvider.DataProvider.expensiveOperationTwo(..);); && target(dp); { int ret = 0; Object[] obj = thisJoinPoint.getArgs();; Object key = "OP2" + Integer.parseInt(String.valueOf(obj[0]);); * Integer.parseInt(String.valueOf(obj[1]););; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(dp);; operationCache.put(key,new Integer(ret););; } return ret; } 3、根据expensiveOperationTwo来修改原来的expensiveOperation's 的pointcut & advice。 int around(DataProvider dp);: execution(* DataProvider.expensiveOperation(..);); && target(dp); { int ret = 0; Object[] obj = thisJoinPoint.getArgs();; Object key = obj[0]; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(dp);; operationCache.put(key,new Integer(ret););; } return ret; } 4、两个方法的pointcut & advice越来越相似,为了把两个pointcut & advice合并,现在就需要我们解决另外一个问题:cache key的生成策略。把它抽取出来,生成一个方法。 private Object getCacheKey(Object[] obj); { Object key = null; if (obj.length == 1); key = obj[0]; else if (obj.length == 2); key = "OP2" + Integer.parseInt(String.valueOf(obj[0]);); * Integer.parseInt(String.valueOf(obj[1]););; return key; } 5、ok,最后合并这两个pointcut & advice。 pointcut expensiveOperation(DataProvider dp); : execution(* DataProvider.expensiveOperation*(..);); && target(dp);; int around(DataProvider dp);: expensiveOperation(dp); { int ret = 0; Object key = getCacheKey(thisJoinPoint.getArgs(););; if (operationCache.containsKey(key);); { Integer val = (Integer); operationCache.get(key);; ret = val.intValue();; } else { ret = proceed(dp);; operationCache.put(key,new Integer(ret););; } return ret; } |
|
返回顶楼 | |