原创作者: Readonly
阅读:2218次
评论:2条
更新时间:2011-05-26
大家好, 偶又回来了, 继续嗡嗡作响的AOP之旅, 废话少说, 先来看看AOP号称给可以带给我们的第一个好东东:
1. Modularized implementation of crosscutting concerns
嘿嘿, 一堆buzzword呢: modularity, crosscutting, concern, 偶来用土话解释一下吧, 就是号称可以把原来需要在N处代码里处理的问题, 移到一处地方来处理.
为了解释这个好处呢, AOP的鼓吹者通常会拿一个日志的例子来说明 (因为它最简单, 最容易实现), 给各位举一个简单的银行帐户的例子:
测试通过了, 但是我们想记录每个方法调用的时间怎么办呀? 每个方法开始的时候和结束的时候各加一句? 作为一个AOPer, 一定会大声的嘲笑你, No No 什么年代了还用这样粗劣的方法? 来看看AOP是怎么做的 (以下以aspectwerkz这个AOP实现为例子):
先写个Aspect (又一个buzzword?):
组合pointcut, advice (又是2个buzzword?)
运行一下, 哇, 看起来好cool呀, 啧啧称奇
慢着, 回到现实世界来吧, 我们做的是实际应用, 不是这样的简单玩具, 象这种简单日志, 不就是一个触发器或拦截器的概念吗, 远古时代就有的东西, 改头换面, 加了一些buzzword, 又出来糊弄人......
实际应用中AOP可以做复杂上下文环境的日志吗, NO! 别拿玩具出来骗小孩了!
接下去来看看AOP号称给可以带给我们的第2个好东东:
2. Once and only once, more code reuse.
这个倒没有什么buzzword了, 而且也是OOP追求的终极目标, 那么来看例子把, 还是上面那个银行帐户的例子, 加了一个列出每个月的帐务清单的方法.
AOPer说这个方法可能比较耗时: 需要从数据库里查询一些记录, 然后封装成String返回, 我们用AOP给它加个Cache来提高性能吧:
AOPer: 看到没? 如果需要cache其他的方法, 那么我们修改一下pontcut的匹配符, 不用修改, 增加其他任何代码, 真正的reuse吧!
哇, 又看起来很cool?
再次回到现实生活来吧, 难道Cache的东西难道不需要刷新吗? 偶今天又往这个帐户里面存了一笔钱, 你的AOP Cache如何得知要清除对应的Key, 从而列出正确的当月清单来? 不同的Instance需要不同的Cache策略, 你的AOP Cache能做到? 实际应用的复杂性不是AOP这种简单的code reuse所能消除的!
AOPer, 找些有实际意义的例子吧, 别老是拿这些幼稚的东东给偶们嘲笑了!
后记: 一个设计良好的OOP架构, 完全可以更简洁的实现上面这些幼稚的功能 ((比如xwork的拦截器), 更重要的是不需要你去了解一堆的buzzword
1. Modularized implementation of crosscutting concerns
嘿嘿, 一堆buzzword呢: modularity, crosscutting, concern, 偶来用土话解释一下吧, 就是号称可以把原来需要在N处代码里处理的问题, 移到一处地方来处理.
为了解释这个好处呢, AOP的鼓吹者通常会拿一个日志的例子来说明 (因为它最简单, 最容易实现), 给各位举一个简单的银行帐户的例子:
package readonly.aopsucks.test; import junit.framework.TestCase; import readonly.aopsucks.bank.Account; import readonly.aopsucks.bank.impl.AccountImpl; public class BankTest extends TestCase { public void testDeposit() { Account a = new AccountImpl(1000); a.deposit(100); assertEquals(1100, a.getBalance()); } }
package readonly.aopsucks.bank; public interface Account { public void deposit(long amount); public void withdraw(long amount); public long getBalance(); }
package readonly.aopsucks.bank.impl; import readonly.aopsucks.bank.Account; public class AccountImpl implements Account { private long balance; public AccountImpl(long balance) { this.balance = balance; } public void deposit(long amount) { balance += amount; } public void withdraw(long amount) { balance -= amount; } public long getBalance() { return balance; } }
测试通过了, 但是我们想记录每个方法调用的时间怎么办呀? 每个方法开始的时候和结束的时候各加一句? 作为一个AOPer, 一定会大声的嘲笑你, No No 什么年代了还用这样粗劣的方法? 来看看AOP是怎么做的 (以下以aspectwerkz这个AOP实现为例子):
先写个Aspect (又一个buzzword?):
import java.util.Date; import org.codehaus.aspectwerkz.joinpoint.JoinPoint; public class LogAspect { public void beforeCalled(JoinPoint joinPoint) { System.out.println((new Date()) + ": before " + joinPoint.getTargetClass().getName() + "." + joinPoint.getSignature().getName() + " called"); } public void afterCalled(JoinPoint joinPoint) { System.out.println((new Date()) + ": after " + joinPoint.getTargetClass().getName() + "." + joinPoint.getSignature().getName() + " called"); } }
组合pointcut, advice (又是2个buzzword?)
<aspectwerkz> <system id="aopsucks"> <package name="readonly.aopsucks.aspect"> <aspect class="LogAspect"> <pointcut name="allMethod" expression="execution(* readonly.aopsucks.bank...*(..))"/> <advice name="beforeCalled" type="before" bind-to="allMethod"/> <advice name="afterCalled" type="after" bind-to="allMethod"/> </aspect> </package> </system> </aspectwerkz>
运行一下, 哇, 看起来好cool呀, 啧啧称奇
慢着, 回到现实世界来吧, 我们做的是实际应用, 不是这样的简单玩具, 象这种简单日志, 不就是一个触发器或拦截器的概念吗, 远古时代就有的东西, 改头换面, 加了一些buzzword, 又出来糊弄人......
实际应用中AOP可以做复杂上下文环境的日志吗, NO! 别拿玩具出来骗小孩了!
接下去来看看AOP号称给可以带给我们的第2个好东东:
2. Once and only once, more code reuse.
这个倒没有什么buzzword了, 而且也是OOP追求的终极目标, 那么来看例子把, 还是上面那个银行帐户的例子, 加了一个列出每个月的帐务清单的方法.
public interface Account { public String generateMonthlyReport(String month); }
AOPer说这个方法可能比较耗时: 需要从数据库里查询一些记录, 然后封装成String返回, 我们用AOP给它加个Cache来提高性能吧:
package readonly.aopsucks.aspect; import java.util.HashMap; import java.util.Map; import org.codehaus.aspectwerkz.joinpoint.JoinPoint; import org.codehaus.aspectwerkz.joinpoint.MethodRtti; public class CacheAspect { private Map cache = new HashMap(); public Object cache(JoinPoint joinPoint) throws Throwable { MethodRtti rtti = (MethodRtti)joinPoint.getRtti(); final Long key = new Long(calculateHash(rtti)); Object cachedValue = cache.get(key); if (cachedValue == null) { cachedValue = joinPoint.proceed(); cache.put(key, cachedValue); }else{ System.out.println("using cache: " + cachedValue); } return cachedValue; } private long calculateHash(MethodRtti rtti) { int result = 17; result = 37 * result + rtti.getName().hashCode(); Object[] parameters = rtti.getParameterValues(); for (int i = 0, j = parameters.length; i < j; i++) { result = 37 * result + parameters[i].hashCode(); } return result; } }
<aspect class="CacheAspect" deployment-model="perInstance"> <pointcut name="cacheAble" expression="execution(* readonly.aopsucks.bank...generateMonthlyReport(..))"/> <advice name="cache" type="around" bind-to="cacheAble"/> </aspect>
AOPer: 看到没? 如果需要cache其他的方法, 那么我们修改一下pontcut的匹配符, 不用修改, 增加其他任何代码, 真正的reuse吧!
哇, 又看起来很cool?
再次回到现实生活来吧, 难道Cache的东西难道不需要刷新吗? 偶今天又往这个帐户里面存了一笔钱, 你的AOP Cache如何得知要清除对应的Key, 从而列出正确的当月清单来? 不同的Instance需要不同的Cache策略, 你的AOP Cache能做到? 实际应用的复杂性不是AOP这种简单的code reuse所能消除的!
AOPer, 找些有实际意义的例子吧, 别老是拿这些幼稚的东东给偶们嘲笑了!
后记: 一个设计良好的OOP架构, 完全可以更简洁的实现上面这些幼稚的功能 ((比如xwork的拦截器), 更重要的是不需要你去了解一堆的buzzword
2 楼 mangolms 2013-01-08 16:32
1 楼 xdyl 2011-07-19 14:24
对于一个SCA的系统来说,不同的子系统之间经常会互相调用.
这些调用的时间统计用Aop来做是目前最合适的方式.
2.缓存的处理用Aop感觉有一些别扭.不过也不是完全不能实现.
可是如果不是用做缓存,用来做参数检查和验证是完全有可能的.