(3.1.6)AOP

本文探讨了面向切面编程(AOP)的概念及其与面向对象编程(OOP)的关系,并详细解析了AOP如何帮助解决代码重复问题。通过Spring框架中的AOP实现,介绍了AOP的关键术语,如切面、通知、切入点等,并通过实例说明了如何利用AOP简化并发访问控制。
摘要由CSDN通过智能技术生成


我们来看一些框架,structs,spring,hibernate,先说structs吧,其本身的目的就是为了达到v/c分离.事实上呢?jsp的目前是什么?为了把servlet的代码c与html标记的v分离.事实如此吗?程序员从servlet转向jsp后立即发现,用servlet输出html是代码中夹杂了大量的标记.而转向了jsp是大量的标记中夹了代码.那么jsp有什么必要存在呢?(一个很好的理由是,让以前做HTML页面的人能进入编程的门槛).

好了,tag-lib来了.解决了c/v分离了吗?先不要说结果,一个已经混乱的场面中已经被值入了一个不知道结果的东西.如果你是纯jsp或纯servlet的,你的程序可以在任何servelt引擎上运行,现在,你的容器至少要手工植入对tag-lib的支持.

结果呢?我们可想而知,如果真的能分离,那何必又出来structs?而structs如果真的能分离又何必出来spring?如此递归,没有一种模式框架能真正解决我们的问题,却不断地给我们植入一个又一个支持包.

什么MVC,什么AOP,什么IOC,不就是"分离"吗?一个雅称叫"解耦".

MVC解的是行为和对象关系之耦.

AOP解的是通用逻辑与主要(特有)逻辑之耦.

IOC解的是调用对象和被调用对象之耦.


1.引言

我们知道,J2EE应用系统只有部署在J2EE容器中才能运行,那么为什么划分为J2EE容器和J2EE应用系统?通过对J2EE容器运行机制的分析,我们可以发现:实际上J2EE容器分离了一般应用系统的一些通用功能,例如事务机制、安全机制以及对象池或线程池等性能优化机制。这些功能机制是每个应用系统都需要的,而且其设计开发有一定难度,同时对运行的稳定性和快速性要求颇高,必须经过长时间调试和运行经验积累而成,因此可以从具体应用系统中分离出来,形成一个通用的框架平台(J2EE容器服务器产品),如Tomcat、JBoss、Websphere、WebLogic等。从J2EE系统划分为J2EE容器和J2EE应用系统两个方面,我们已经看到一种分散关注的思路(separation of concerns)。

分散关注:将通用需求功能从不相关类之中分离出来;同时,能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以。AOP就是这种实现分散关注的编程方法,它将“关注”封装在“切面”中。

2.AOP与OOP的关系

AOP是面向切面的编程,但它具体的含义是什么?AOP出来之后是不是要取代原来的OOP? --> AOP是Aspect Oriented Program的首字母缩写,是面向切面编程的意思。我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去(给不同的类设计不同的方法),这在软件设计中往往称为职责分配。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。但是人们也发现,在分散了代码的同时也增加了代码的重复性:比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容(也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来)。有人也许会说,那好办啊,我们可以将这段代码写一个独立的类独立的方法里,然后再在这两个类中调用,但是这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。

这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。从技术上来说,AOP基本上是通过代理机制实现的

3.概述

AOP是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP算是这种目标的一种实现。举例:某一个操作在各个模块中都有涉及,这个操作就可以看成“横切”存在于系统当中。在许多情况下,这些操作都是与业务逻辑相关性不强或者不属于逻辑操作的必须部分,而面向对象的方法很难对这种情况做出处理。AOP则将这些操作与业务逻辑分离,使程序员在编写程序时可以专注于业务逻辑的处理,而利用AOP将贯穿于各个模块间的横切关注点自动耦合进来。AOP的核心思想就是将应用程序中的业务逻辑处理部分同对其提供支持的通用服务(即所谓的“横切关注点”)进行分离,这些“横切关注点”贯穿了程序中的多个纵向模块的需求。

使用AOP机制进行开发,首先要将需求分解成一般关注点和横切关注点(即将核心模块级的业务关注点和系统级的横切关注点分离);然后各自独立的实现这些关注点;最后用工具(即proxy)将业务逻辑代码和横切关注点代码编织到一起,形成最终的程序。

4.AOP与Spring

Spring框架中所提供的AOP支持,是基于动态Proxy实现的,在目标对象的方法调用前后插入相应的处理代码。AOP代理可以是基于JDK动态代理,也可以是基于CGLIB代理。Spring默认使用的是基于JDK动态代理实现,这样任何的接口都能被代理。基于Spirng框架的应用程序开发,程序员会有一种自然的倾向性来实现面向接口编程而不是类,业务对象通常也是实现一个或者多个接口,这也是一种良好的编程习惯。Spring也可以基于CGLIB实现AOP代理,这样所代理的是类而不是接口。如果一个业务对象没有实现某一个接口,那么CGLIB将被使用。

5.名词解释

(1)切面(aspect):横切多个对象形成的横切关注点。如日志记录/事务管理/权限管理。由Spring的advisor、struts2的interceptor、servlet的filter实现。

(2)通知(advice):切面的实际实现。advice在pointcut插入到应用系统中。许多AOP框架包括Spring都是以拦截器/拦截器链做通知模型。通知类型主要有:

Around通知:包围一个pointcut的通知,这是最强大的通知。around通知在方法调用前后完成自定义的行为。

Before通知:在一个pointcut之前执行的通知。

AfterReturning通知:在pointcut正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。

Throws通知:在方法抛出异常时执行的通知。

(3)切入点(Pointcut):advice被插入的地点(即拦截点,我们可以在拦截点前后插入advice)。

Spring只支持方法拦截,也就是说,只能在方法的前后进行拦截,而不能在属性前后进行拦截;

Spring支持四种切入点(拦截类型):目标方法调用前(前置拦截的类必须实现MethodBeforeAdvice接口,实现其中的before方法),目标方法调用后(后置拦截的类必须实现AfterReturningAdvice接口,实现其中的afterReturning方法),目标方法调用前后(前后拦截的类必须实现MethodInterceptor接口,实现其中的invoke方法。前后拦截是唯一可以控制目标方法是否被真正调用的拦截类型,也可以控制返回对象。而前置拦截或后置拦截不能控制,它们不能印象目标方法的调用和返回),以及目标方法抛出异常(throw)。Spring拦截器的缺点:不能对于特定方法进行拦截,而只能对某个类的全部方法作拦截。

PointCut的定义相当于更加细化地规定了哪些方法被哪些拦截器所拦截,而并非所有的方法都被所有的拦截器所拦截。在ProxyFactoryBean的属性中,interceptorNames属性的对象详细定义了 PointCut和Advice的对应关系,比如常见的基于名字的切入点匹配(NameMatchMethodPointcutAdvisor类)和基于正则表达式的切入点匹配(RegExpPointcutAdvisor类)。这些切入点都属于”静态切入点“,因为他们只在代理创建的时候被创建一次,而不是每次运行都创建。

PointCut的概念是AOP的关键,使AOP区别于其它使用拦截的技术。PointCut使Advice独立于OO的层次选定目标,转而更细致的在method层面选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。

(4)目标对象(Target Object):被通知(被代理)对象。

(5)代理对象(Proxy Object):将advice应用到目标对象后创建的对象。

(6)织入(Weaving):将切面应用到目标对象从而创建一个代理对象的过程。

6.举例:假设有在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个数据封装在数据对象中,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。为了完成上述并发访问同一资源的功能,需要引入锁Lock的概念,也就是说,某个时刻,当有一个访问类访问这个数据对象时,这个数据对象必须上锁Locked,用完后就立即解锁unLocked,再供其它访问类访问。使用传统的编程习惯,我们会创建一个接口,所有的访问类继承这个接口,如下:

  1. public interface BaseServlet {
  2.   abstract void locked();
  3.   abstract void accessDataObject();
  4.   abstract void unlocked();
  5. }

缺点:accessDataObject()方法需要有“锁”状态之类的相关代码;继承于BaseServlet接口的每个类都要重复实现locked()和unlocked()方法,重复代码很多。

仔细研究这个应用的“锁”,它其实有下列特性:“锁”功能不是具体访问类的首要或主要功能,访问类主要功能是访问数据对象,例如读取数据或更改动作;“锁”行为其实是和具体访问类的主要功能可以独立、区分开来的;“锁”功能其实是这个系统的一个纵向切面,涉及许多类、许多类的方法。如下图:

 

因此,一个新的程序结构应该是关注系统的纵向切面,例如这个应用的“锁”功能,这个新的程序结构就是aspect。在这个应用中,“锁”的aspect应该有以下职责:提供一些必备的功能,对被访问对象实现加锁或解锁功能。以保证所有在修改数据对象的操作之前能够调用lock()加锁,在它使用完成后,调用unlock()解锁。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值