AOP切面编程 几年前写的,移过来
在2010年04月的经验交流会上,XXX工程师提及到使用AOP实现数据库连接自动控制这一技巧,但当时大部分同事都没有反应,可能是对AOP不太熟悉,所以我想在这里介绍以下AOP:什么是AOP、AOP能带给我们什么。首先我可以保证的是,AOP是一个非常牛逼的设计思维,认识到AOP的优点将对你的程序设计思维的提升有很大帮助。
过滤器
我们首先来了解一个看上去跟AOP毫无关系的java类:servlet。Servlet是java一类class的总称,它实现了java的http支持。Servlet运行在servlet容器内,容器把http的请求按一定的规则路由到特定servlet,servlet处理这些请求并返回响应(jsp也就是servlet的一种)。Servlet看似平淡的设计在java 1.3时发生了变化:在servlet处理请求之前,添加了一类称为过滤器的对象。使用这类对象可过滤处理http请求以及修改servlet返回的响应:例如过滤非法ip请求、处理数据完整性、修改servlet返回内容用以重定向、甚至可以把原有的请求路由到另外一个对象来处理。
过滤器的出现大大提高的servlet的灵活性,过滤器其实就是我们熟悉的chain of responsibility模式。由于过滤器能大大提高系统的灵活性,所有越来越多的软件模块都加入了过滤器设计,于是,程序设计者发现,如果要使用这种过滤器模式,你不得不一次又一次地编写几乎相同的代码:过滤器的框架代码,很显然,这违反了代码复用原则。
回过头来看,很可笑,首先,我们为了提高代码复用,引入了过滤器模式,由于过滤器模式频繁使用,出现冗余代码,于是问题又回到了原点之上,如何提高代码的复用性,不过这次是从具体代码转移到框架代码上。
通用拦截过滤器-AOP
过滤器模式还存在一个问题,就是使用过滤器的地方,必须要硬编码:要加入过滤器,你必须在原有函数调用之前放置类似的语句:filter.doFilter(),并且你总要有个地方可以setFilter()。如果我们把调用语句filter.doFilter()的地方称为拦截点的话,那么传统的过滤器模式的缺点就显而易见:拦截点硬编码!硬编码影响软件系统的扩展性,有没有无需硬编码拦截点的过滤器模式?有,它就是AOP。
AOP――无需硬编码拦截点的过滤器模式,并在该模式上进行了扩展,成为崭新程序设计理论。AOP就是:连接点(JoinPoint)、拦截点(PointCut)及通知(Advice)。连接点就是系统各模块之间的动作,具体来说就是一个函数调用,或者一个属性访问;拦截点就是你拦截的动作;通知就是你的过滤器。支持AOP的语言,你直接定义一个过滤器及拦截策略,语言就会自动按拦截策略完成拦截,而一个不支持AOP的语言,你需要手动编写过滤器模式的所有代码:包括过滤器容器,编码实现拦截策略等。而更重要的一点是,AOP的拦截策略使用模式匹配方式,能实现拦截一系列相似的动作而不是某个特定的动作,这一系列相似的动作可以看做系统的一个面、一个切面、一个方面,所以AOP全称就是面向方面的编程(aspect-oriented programming),我更喜欢称其为切面编程。例如,系统在处理客户请求的业务对象的成员函可能是一类HandleXXXRequest,这类函数描述了系统的一个方面:处理客户请求,AOP可对这类函数进行统一拦截。又例如,系统在处理数据持久化的DAO内可以会有WriteXXX,ReadXXX等函数,这类函数描述的方面:就是系统的数据持久化,AOP可对这类函数进行统一拦截。
AOP旨在解决OOP无法解决的问题(没错,OOP不是万能,但由于我们接触OOP时间太长了,OOP的缺点接受不了,慢慢就学会了享受-_-!!!),AOP就是对系统内各个方面进行编程而OOP却不能,例如系统内一个安全检测对象会在任意一个业务对象内被使用,以检测请求者是否合法、请求数据是否安全,这是系统安全方面的处理逻辑。这会导致众多业务对象内出现这样类似的调用secure.check(request),这个语句频繁出现显得冗余,却无法消除,一个可行的OOP解决方法是抽取出一个业务对象基类,把安全检测发生点提升到基类处理,具体的派生类无需处理。其实,这样做只是用OOP手法实现了过滤器,它仍然需要硬编码业务对象基类。一个更要命的情况是,有很多种安全检测对象负责不同方面的安全性检查,例如secureReq.checkReq(request),secureRsp.checkRsp(response),securePrs.checkPrs(persistence)遍布系统每个角落,你如何使用OOP来统一处理?AOP把这些遍布系统每个角落的、负责某方面处理的逻辑称为crosscutting concern,典型的crosscutting concern有安全检测、事务处理、同步、性能监视等,使用AOP,你可以对系统任何应用到这些crosscutting concern的地方实施拦截,以进行统一处理,消除遍布系统每个角落的冗余的代码。
AOP给我们带来了:1.能使一个业务逻辑的实现无需依赖于这个业务逻辑的上下文,使这个业务逻辑实现得更抽象,粒度更细,以提高复用性,而这个业务逻辑的上下文由AOP提供,例如最常见的数据库连接:在DAO内你完全不需要编写任何获取数据库连接的代码(你不用理会这个连接应怎样创建,是本地创建连接、是远程连接还是通过JNDI的LookUp来获取),数据库连接由AOP建立、提交(回滚)及关闭。2.实现proxy(decorator)模式,为原有的行为添加新功能或改变原有的行为,传统的proxy需要编码实现,但AOP提供了语言层面的支持,无需编码实现,在本文的“AOP实现原理”一节可以看到proxy模式。3.实现observer模式,当目标状态改变时,观察者都得到通知,AOP的Advice正如其名。同样地,AOP无需编码实现observer,因为这个模式在语言层面已经支持。4.实现chain of responsibility模式,正如本文开头所介绍的,AOP下的chain of responsibility仍然是无需编码实现的。5.也是最重要的,AOP引入了Interceptor模式,这个模式可以描述及处理系统内众多OOP无法处理的crosscutting concern。
AOP实现原理
介绍完AOP的功能后,大家可能很有兴趣了解一下AOP是如何实现的,接下来我将使用Spring AOP作为例子介绍AOP的实现原理。在介绍Spring AOP之前,必须了解一下Spring IoC容器,因为IoC容器是Spring AOP的基础。简单地说,IoC容器控制对象创建(ObjectFactory),把对象之间的关系从硬代码编写转换成可配置。例如一般情况创建对象是这样的
new Person(new Name(“name”),new Age(3)); |
这里创建一个Person对象需要一个Name及Age对象,也就是说Person依赖Name和Age。但通过IoC来创建的话将会是这样
Person person = iocContainer.getBean("AName3"); |
并且person的name和age都通过配置文件指定。
<bean id="AName3" class="Person"> <constructor-arg index="0" ref="AName" /> <constructor-arg index="1" ref="Age3" /> </bean>
<bean id="AName" class="Name"> <constructor-arg value="name" /> </bean>
<bean id="Age3" class="Age"> <constructor-arg value="3" /> </bean> |
更神奇的是,person是否真的是一个Person都很难确定,说不定它只是一个AI而已。这并不是在开玩笑,由于IoC容器控制了对象的创建,所以IoC返回给我们的对象很可能是我们所要求的对象的派生类对象!Spring就是使用这个特性实现AOP拦截的。例如上述例子,IoC首先创建person对象,然后检查person对象的成员函数是否符合拦截策略,如果符合,IoC会再次动态创建Person类的派生类,并根据派生类创建对象,暂称这个对象proxy,IoC用proxy保存已定义Advice(你的过滤器),包裹person并实现person的所有函数,这些函数会首先调用Advice再调用person,这样就实现了对person的拦截。具体的流程看下图:
Spring这种方式只是AOP实现的其中一种。在AOP领域,老大是AspectJ,AspectJ既是程序框架,也是一门语言,它扩是展了Java语法的一门AOP语言,同时它又是AOP理论的始祖。AspectJ通过编译器及JVM特性,支持静态拦截即编译时拦截、加载时拦截、方法拦截、属性拦截、构造器拦截,比Spring要丰富很多。
C++的AOP?
AOP如此吸引,在C++领域有否实现?其实要C++编译器提供编译时拦截能力是不困难的,C++的AOP已经由现成品AspectC++,有兴趣的可以访问http://www.aspectc.org获取信息。但是要C++支持动态拦截基本上是不可能的了,不过,C++不能,但操作系统却能提供动态拦截能力,在windows操作系统上,动态拦截能力由微软的detour程序库提供。但detour不能说是AOP的实现,首先,detour是拦截win32函数的手段,而不是一套完整的理论;其次,detour拦截的是单个函数,而不是一个面,更形象地说,AOP为切面编程,那么detour只能是切点编程,类似的hook技术也跟detour一样,只能对点编程而不是对面编程;最后,detour也是属于硬编码拦截,它没有提供一套完整的机制来描述系统内的动作及拦截动作。这里附上detour拦截原理图:
总结,AOP是崭新的程序设计理论及方法,该理论建立于上世纪90年代中并在2002年提供了第一份实现品:AspectJ。理解并尝试使用AOP,你的视野将会得到扩展,因为AOP给了你一个崭新的、之前从未有过的观察点。最后,推荐两本AOP的书《Spring in Action》和《AspectJ in Action》,其中,《Spring in Action》除了讲述AOP之外,还介绍到IoC理论,IoC理论也是一个崭新的程序设计思维,强烈推荐。
题外话,很多人认为AOP只不过是记日志用的,其实这里存在误会。可能因为最初介绍AOP的文章内用了记日志的例子而导致这个误会产生的,没错,AOP可以用来记日志,但AOP更深层的理念是拦截、一种统一方式的拦截,通过AOP,你得到了一种软件设计的新思维。另外,本文也提到IoC,IoC就是把对象关系从硬编码方式中解放出来,变为可配置的一项技术,它的全称为控制反转(Inversion of Control),又可以称为依赖注入(Dependency Injection)。
posted on 2013-03-27 22:09 rickerliang 阅读(486) 评论(0) 编辑 收藏 举报