AOP相关讨论

AOP目前在国外很热,几乎逢人必讲,围绕AOP进行相关讨论,促进大家理解运用这个新型设计理念,banq的开辟了专门文章栏,可以针对这些文章进行讨论交流,也可以贴出容易理解的英文资源。banq的文章如下:

http://www.jdon.com/design.htm

之前我在论坛中发言,反复强调,不要自己做数据库连接池,不要自己做事务机制,因为运行时,一个资源的lock和unlock是非常复杂的,Java语言本身提供的一些机制无法优雅彻底地解决“锁”问题,所以,再高的高手也局限于语言工具本身。

如果你觉得很不爽,还是要坚持自己做这些底层的东西(而且必须做好),那么先来学AOP吧。

理解AOP并不容易噢....

AOP的确是个不错的概念,以下是我前段时间写的一个简介,不知道我理解的是否正确?

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

Aspect Orient Programming ,简称 AOP ,即面向方面编程,再次成为程序员们关注的焦点,《Dr.Dobb's 软件研发》杂志第四辑对AOP做了专题介绍。
何为AOP呢?就我个人理解,简单说就是将一些散布于各处(类)的重复的功能,比如安全性、身份验证、数据服务等(术语横切),集中定义,原来的类中则不必重复编写相似的编码,在程序编译后,由编译器将各横切业务嵌入到预先定义的各个“连接点”。
这听起来好像很难理解,但看过一段具体代码后就真相大白了,目前支持AOP的语言比较少,AspectJ是其中一种,它是对Java语言的扩充,Jboss等一批优秀的系统都已经支持AspectJ,以下代码来自《Dr.Dobb's 软件研发》,限于篇幅只截取了一部分且作了修改,语言环境AspectJ:

class A
{
int a(int x)
{
System.out.println("Call method 'A.a()'")"
}
}
以上是一个普通的java类,下面请看方面代码Showcase.java:
public aspect Showcase //声明Showcase 是一个方面(aspect)
{
pointcut int_A_a_int(): call(int A.a(int)) //声明一个切入点,其入口为类A的方法 a()
before():int_A_a_int() //在切入点int_A_a_int()之前会执行的语句
{
System.out.println("Before:Call A.a()")"
}
after():int_A_a_int() //在切入点int_A_a_int()之后会执行的语句
{
System.out.println("After:Call A.a()")
}
}
以上代码中:关键字aspect,before(),pointcut,after()都是aspectJ对java的扩展
以上代码在运行时会产生如下输出:

Before:Call A.a()
Call method 'A.a()'
After:Call A.a()
通过以上代码大家可以略微了解一点AOP的特性了吧?
AOP并不是一种新的东西,它只是OOP的有益补充,我们仍然使用OOP来完成我们的大部分设计,AOP只关注其中15%的内容。
AOP也不是刚刚出现的概念、方法,追朔其历史至少在7、8年前就已经得到应用了,只不过当时不叫AOP。微软早在设计MTS,com+等技术的时候就大量使用了AOP的概念,当然,那时候也还没有现在这个正式名称-----AOP(Aspect Orient Programming)
当然,AOP目前还没有形成完整强大的体系结构,也仅有可陈的几种开发工具、语言支持这一概念。但各大软件巨头包括微软对此已技术的关注,使我们有理由相信:AOP必将成为继面向对象技术产生以来最值得等待的一次革新。

我看看主题监控

什么监控?不要胡搞好不好?

论坛监控到底好不好用!

AOP可以说类似主题监控,它是从一个纵向切面提供了一些功能,那么这些功能是如何被触发运行的?这就是类似观察者模式,主题监控可以说属于AOP的触发部分。

AOP确实不是新的概念,很多人已经知觉不知觉地使用了,例如J道的EJB方法调用框架,提供了AOP实现的底层基础,由于本框架基于JDK动态代理实现,从而提供了纵向方面切入点。可以由开发者根据自己的要求,在调用EJ具体功能B之前,加入有关自己定义的Advice。


http://www.jdon.com/product/ejbinvoker.htm

呵呵~原来这就是AOP啊~

前一段时间写的授权框架就是使用了动态Proxy,还自以为“另辟蹊径”,原来如此,呵呵~``惭愧啊~

看到banq在首页上那篇文章:
“nanning 这是以中国南宁命名的一个项目,搞不清楚为什么和中国相关?是中国人发起的?”

nanning的作者Jon Tirsen曾经到中国旅游,觉得广西简直是人间仙境,于是用南宁来命名自己的作品。得亏他当时没到杭州……

另外,Spring的AOP是基于动态代理的。

to gigix
原来是这样,gigix在“程序员”杂志曾经发表关于AOP专题,能不能在这里多贴一些宝贵资源或见解,供更多学习?

因为目前类如权限控制等这些通用功能想自己做的人不在少数,AOP为他们提供了一个模式。

to Kitta
使用动态代理实现权限控制是最有效的做法,但是这只能说是提供了AOP底层机制,还需要提供两个接口实现:pointcut和Advice,就象模式一样,符合这样定义,才可认为是真正的AOP,否则算AOP-like

AOP如雷贯耳久矣。虽然看到过一些logging的例子,一直希望能看到更多的例子。难得banq如此热心地给了几个简单易懂的例子,我也来胡言乱语两句。

我现在的问题是,仍然对aop和普通的OO patterns的区别很疑惑。在csdn上我曾经自己根据自己的猜测大放厥词,本希望有达人指正,可惜可能我错的太厉害,根本没人理我。:<
http://expert.csdn.net/Expert/topic/2507/2507183.xml?temp=2.181643E-02


对于那个ejb container的比喻,我不知道这到底应该算是经典的OO decorator, adaptor, bridge模式,还是它有它自己的独特的性质要称为ejb。一般来说,一个bridge模式,
interface Published{
void useIt();
}
如果这个接口是作为最终的用户调用的接口,那么,
interface ToImplement{
void implementIt();
}
这个接口作为框架开放给具体的实现者的接口,是实现者把自己的逻辑插入框架的规范。

于是,框架可以提供
public final class Facade{
public static Published getPublished(ToImplement ti){...}
}
来把实现者的实现封装转换为用户期待的最终接口Published。

我想ejb container应该是这种类似的结构。


那个lock的aspectJ例子,如果用这个bridge模式,可以依样葫芦如此:
interface Workable extends Runnable{
//这个接口凑巧正好同时是Published和ToImplement
public boolean createData();
public boolean updateData();
}
可以把文中的aspect的代码完全移入一个bridge class如下:

import EDU.oswego.cs.dl.util.concurrent.*;

final class LockedWorker implements Workable {

  ......
  ReentrantWriterPreferenceReadWriteLock rwl = 
    new ReentrantWriterPreferenceReadWriteLock();

private final Workable worker;
LockedWorker(Workable w){this.worker=w;}
  public boolean createData(){
beforeWrite();
try{
return worker.createDate();
}
finally{afterWrite();}
}
  public boolean updateData(){
beforeWrite();
try{
return worker.updateDate();
}
finally{afterWrite();}
}
  private beforeWrite() {
    rwl.writeLock().acquire();//上锁 advice body
  }

  private afterWrite() {
     rwl.writeLock().release(); //解锁 advice body
  }

  ......
}

最后,一个Facade:

final class WorkerFacade{
public static Workable getLocked(Workable w){
return new LockedWorker(w);
}
}

后面的那个introduce的例子也可以同样做到。

那么,aspectJ在此到底给我们提供了什么额外的好处呢?

我所能看到的好处:
1。程序员一旦熟悉了aspectJ的语法语义,那么就不必擅长这些bridge的设计方法。

2。aspectJ语法上提供了一点便利。

3。可以任意插代码到某一个类中,不必定义接口。方便直观。

坏处:
1。需要学习aspectJ语法语义。

2。破坏封装,某些XXXWorker类也许是某个包或者类私有的,有些可能是个anonymous class。

3。允许introduce新的field有点太强大了。这很可能引入版本管理的问题。比如说,我作为一个类的实现者,在对某一个aspect的切入不知情的情况下,可能加入了一个同名field。这等于在aspect代码和被切代码之间加入了双向耦合。

4。before()和after()要做的就是加入副作用,象lock这种副作用被切入的类感知不到,也就是说,加入的副作用不与被切入的模块所关心的语义相交。
但是,这并没有强制保障。如果pointcut的选择可以对任意的一个方法切割,而advice可能对一些被切入模块也感兴趣的全局变量做修改,则编织的结果就可能是一场噩梦,很难预测结果,考虑到被切入模块的发展,结果就更难以收拾。
于是,aspect的引入可能是一个pandora box,被不当切割的整个系统就会是一团乱麻。这是因为aspect打破了封装和模块的界限,你可以把两个独立的系统杂乱的编织在一起。
相比之下,用bridge的方式,因为你需要明确定义两者之间的接口和语义,模块的通信只在狭窄的接口上,这个问题就明显容易控制得多。

5。对pointcut的声明需要定义在aspect中,而很多的时候,对aspect的使用应该由使用者决定,而不是一旦决定类C使用Aspect A, 就必须要在aspect代码中加入:我要切割类C。
换句话说,最好能由一个第三方来决定aspect到被切割类的映射。

对了,对aspectJ我还有一些问题想请教banq:
1。如果对一个类的一个pointcut有多于一个的aspect,那么这些advice的织入遵循什么样的顺序呢?还是说顺序不确定?

2。象那个lock的例子,aspect说要在createData()和updateData()前后都插入代码,那么,如果createData()内部调用了updateData()或者自己递归调用了(可以直接也可能间接),如:
boolean createData(){
...
return updateData();//这里插入代码吗?
}
boolean updateData(){
...
return X.update(this);//间接的递归
}
class X{
public static boolean update(Data d)
{return d.updateData();}
//插入代码吗?
}

如果三言两语说不清楚,没关系,告诉我,我自己找资料吧。

我来抛砖引玉

aspectJ 是一种语言级别的静态aop,学习曲线很陡,需要对原有的代码做修改,是一种“侵入性”很强的解决方案

而现今aop的主流是动态aop,无需对现有代码做修改,只需增加配置文件,或新的类,就可以对现有的类进行改造。

我个人比较倾向于动态。

动态之中包括aspectweze,naning,jboss,spring,其中aspectwerkz 是最灵活的。
spring本来是自己做,后来改用naning,另国内的一个magic container也用nanning,看来是爱国啊。
一些简单的比较见
http://www.mackmo.com/nick/blog/java/?permalink=aop-frameworks-review.txt&page=trackback

jboss的方案是最简单的,但是它的pointcut太单薄的了,不能满足基本的需要,richard也毫不留情的痛斥jboss aop,做些简单的玩玩考虑jboss aop

nanning 是用dynamic proxies ,理论上快于jboss,和aspectwerkz ,但是现在jvm的速度很快,几乎是没有影响,哪位达人能做做测试请给出结果,这个不作判断。

aspectwerkz 自0.8之后已很成熟了,支持jvm内和跨jvm aop,非常强大。
(作者好像是weblogic工程师,所以在weblogic上使用是没问题的)

另weblogic也退出自己的aop,但是是verdor lock,不予考虑。

先说这么多!