AOP学习笔记.docx
- 文档编号:11445121
- 上传时间:2023-03-01
- 格式:DOCX
- 页数:65
- 大小:101.81KB
AOP学习笔记.docx
《AOP学习笔记.docx》由会员分享,可在线阅读,更多相关《AOP学习笔记.docx(65页珍藏版)》请在冰豆网上搜索。
AOP学习笔记
1、Spring的IoC(InversionofControl)。
这是Spring中得有特点的一部份。
IoC又被翻译成“控制反转”,也不知道是谁翻译得这么别扭,感觉很深奥的词。
其实,原理很简单,用一句通俗的话来说:
就是用XML来定义生成的对象。
IoC其实是一种设计模式,Spring只是实现了这种设计模式。
这种设计模式是怎么来的呢?
是实践中逐渐形成的。
第一阶段:
用普通的无模式来写Java程序。
一般初学者都要经过这个阶段。
第二阶段:
频繁的开始使用接口,这时,接口一般都会伴随着使用工厂模式。
第三阶段:
使用IoC模式。
工厂模式还不够好:
(1)因为的类的生成代码写死在程序里,如果你要换一个子类,就要修改工厂方法。
(2)一个接口常常意味着一个生成工厂,会多出很多工厂类。
可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java的“反射”编程,根据XML中给出的类名生成相应的对象。
从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
IoC中最基本的Java技术就是“反射”编程。
反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。
这种编程方式可以让对象在生成时才决定要生成哪一种对象。
我在最近的一个项目也用到了反射,当时是给出一个.properties文本文件,里面写了一些全类名(包名+类名),然后,要根据这些全类名在程序中生成它们的对象。
反射的应用是很广泛的,象Hibernate、String中都是用“反射”做为最基本的技术手段。
在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普通应用开来的原因。
但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。
所以要理解IoC,你必须先了解工厂模式和反射编程,否则对它产生的前因后果和实现原理都是无法理解透彻的。
只要你理解了这一点,你自己也完全可以自己在程序中实现一个IoC框架,只不是这还要涉及到XML解析等其他知识,稍微麻烦一些。
IoC最大的好处是什么?
因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是现实于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(有点象USB接口和SCIS硬盘了)。
IoC最大的缺点是什么?
(1)生成一个对象的步骤变复杂了(其实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观。
(2)对象生成因为是使用反射编程,在效率上有些损耗。
但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。
(3)缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了,这似乎是所有XML方式的缺憾所在。
总的来说IoC无论原理和实现都还算是很简单的。
一些人曾认为IoC没什么实际作用,这种说法是可以理解的,因为如果你在编程中很少使用接口,或很少使用工厂模式,那么你根本就没有使用IoC的强烈需要,也不会体会到IoC可贵之处。
有些人也说要消除工厂模式、单例模式,但是都语焉不详、人云亦云。
但如果你看到IoC模式和用上Spring,那么工厂模式和单例模式的确基本上可以不用了。
但它消失了吗?
没有!
Spring的IoC实现本身就是一个大工厂,其中也包含了单例对象生成方式,只要用一个设置就可以让对象生成由普通方式变单一实例方式,非常之简单。
总结:
(1)IoC原理很简单,作用的针对性也很强,不要把它看得很玄乎。
(2)要理解IoC,首先要了解“工厂、接口、反射”这些概念。
2、AOP
AOP又称面向方面编程,它的实现原理还是用了反射:
通过对某一个种类的方法名做监控来实现统一处理。
比如:
监控以“insert”字符串开头的方法名,在这种方法执行的前后进行某种处理(数据库事务等)。
但这里我有一个疑问?
不一定所有以insert开头的方法都是数据库操作,哪么当某个insert开头的方法不是数据库操作,你又对它进行了数据事务的操作,这样的错误如何防止?
?
?
我对这方面了解不深,还是只知道一个大概。
3、有一些全系统范围的共同的关注点例如日志记录,用户认证,数据持久,以及其他一些对很多核心业务模块来说共同的要素。
分布在多个模块中的全系统范围特性我们称之为:
横切关注点。
面向方面编程(AOP)就是处理这些横切关注点。
AOP是一套新的方法,通过提供一种新的能够能够横切其他模块的模块化单位:
aspect(方面),达到了分隔横切关注点的目的。
在AOP中,你在aspect中实现横切关注点,而不是把他们融合到核心模块当中去。
一个类似于编译器的东东:
aspectweaver(方面编织器),通过一个称为weaving(织入)的过程把核心模块和横切模块合并到一起,从而构造出最终的实际系统。
最终,AOP用一种边界清晰的方式把横切关注点模块化,产生出一个更容易设计,实现和维护的系统架构。
有一点需要被强调清楚:
AOP不是对糟糕的或者不足的设计的一种矫正。
实际上,在一个设计的很糟糕的核心系统中去实现横切关注点是很鲁莽的行为。
你将仍然需要使用像OOP这样传统的方法来创建一个坚实的核心架构。
AOP所提供的并不是一个完整的全新设计过程,而是一个额外的方案,让设计师能够处理潜在的未来需求而不用破坏整个核心架构,并且在项目的初始阶段也能够更容易地处理那些横切性关注点,因为它们能够在需要的时候被织入系统中而不会危及原来的设计。
用演化的角度来看编程方法,过程性编程引入了功能抽象,OOP引入了对象抽象,而现在AOP引入了关注点抽象。
当前,OOP是绝大多数新软件开发项目所选择的方法,OOP的力量来自于共同行为的建模。
但是,就像我们即将要看到的,并且也许是你已经体会到的,它在处理那些分布于许多个通常还是没有关联的模块之间的行为上做得并不好。
AOP填补了这一空白。
关注点,是一个为了满足系统整体目标必须被处理的特定需求或考虑。
一个软件系统是一组关注点的实现。
例如一个银行系统是以下这些关注点的实现:
客户与账户管理、利息计算、银行间事务、自动柜员机事务、所有实体的保持、访问不同服务的认证、账单生成、客户关怀等等。
除了系统关注点以外,软件系统还需要处理过程性关注点,例如可理解性、可维护性、可跟踪性和易于演进。
正像我们在本章开头看到的例子那样,关注点可以被分为两种类型:
核心关注点:
捕捉模块的中心功能;以及横切关注点,捕捉横跨多个模块的系统级外围需求。
一个典型的企业应用需要处理的横切关注点例如:
用户认证、记录日志、资源池、系统管理、性能、存储管理、数据保持、安全保密、多线程安全、事务完整性、错误检验、策略实施,以及更多。
所有这些关注点都横切了数个子系统,例如日志记录影响到系统中每一个重要的模块,认证机制影响到所有需要访问控制的模块,存储管理影响到每一个有状态的业务对象。
图1.1显示了这些关注点在一个典型应用当中通常如何相互作用。
这张图显示出系统中的实现模块各自都处理系统级和业务的关注点,表现出用当前的实现技术,系统由许多互相纠缠在一起的关注点组合而成,这样关注点的独立性就不能维持了。
辨认系统关注点
通过辨认出系统中核心性和横切性的关注点,我们可以集中力量处理互相分离的单独的关注点,并减少设计和实现的总体复杂度。
为了做到这一点,第一步需要通过划分关注点来分解一组需求。
图1.2使用光线通过棱镜的比喻说明了把需求分解为一组关注点的过程。
我们让一道需求的光线通过一个关注点辨认的棱镜,然后可以看到各个关注点被分离了。
虽然每个需求最初看上去都是一个单独的个体,通过应用关注点辨认的过程,我们可以分离出满足需求所需的独立的核心和横切关注点。
用另一种方式来看一个系统中关注点的分解,想象你把它们投影在一个关注点空间中,这是一个N维的空间,每一个关注点构成一个维度。
图1.3显示了一个三个维度的关注点空间,三个维度分别是核心关注点:
商业逻辑和两个横切关注点:
日志记录和数据保持,这样一个对系统的观察方式的重要之处在于,它显示出了在一个多维空间当中,每个关注点是互相独立的,可以不影响其它部分而独立演化,例如,数据保持的需求从关系型数据库变成对象数据库,这不应该影响到业务逻辑和安全保密的需求。
分离和辨认一个系统中的关注点是开发软件系统当中的一个重要练习,无论所使用的是那种方法。
一旦我们完成了这一点,我们就可以独立地来处理各个关注点,使得设计任务更加可管理。
而当我们要把关注点实现到模块中去的时候,问题就出现了。
理想情况是,我们的实现可以保持关注点之间的这种独立性,但是这并不会经常发生。
一维的解决方案
横切关注点的本质决定了它们要横跨多个模块,当前的实现技术倾向于把它们混合到各个独立的核心模块中去。
为了说明这一点,图1.4显示了一个三维的关注点空间,然而实现这些关注点的代码是一个连续的调用流程,在这种意义上它是一维的,维度的这种不匹配导致了从关注点到代码的映射十分笨拙。
由于实现的空间是一维的,它的主要目标通常集中在实现核心关注点上,而横切关注点的实现就被混杂于其中。
虽然在设计阶段,我们可以很自然地把单独的需求分离成互相独立的关注点,然而在实现阶段,当前的编程技术不能让我们保持这种分离。
重要的是模块化
有一个被广泛接受的假设,处理复杂性最好的方法就是简化它。
在软件设计中,简化复杂系统最好的方法就是辨认出关键点然后把它们模块化。
实际上,OOP方法被开发出来就是作为对模块化软件系统关注点的需求的反应。
但是现实是,尽管OOP方法擅长模块化核心关注点,但它在模块化横切关注点方面却很欠缺。
而开发AOP方法就是为了处理这一欠缺。
AOP中模块化横切关注点的方法是,首先把系统中每个部分辨认为一个明确的角色,然后把每个角色用单独的模块实现,最后把每个模块与有限数量的其他模块松散地耦合。
在OOP中,核心模块可以通过接口(interfaces)松散耦合起来,但是对于横切关注点却没有什么简单的方法达到同样的目的。
这是因为一个关注点是由两个部分实现的:
服务器端部分和客户端部分。
(这里的“服务器”和“客户”的用法是在经典OOP中的意义,分别表示提供某些服务的对象和使用某些服务的对象。
不要和网络中的服务器和客户机混淆起来。
)OOP使用类和接口能够很好地模块化服务器部分,然而,如果关注点属于横切性的,这时由对服务器的调用构成的客户端部分,将会分布于所有的客户端中。
好吧来看例子,OOP中对横切性关注点的典型实现方式是这样:
一个用户认证模块使用抽象接口提供服务,接口的使用使得客户端与接口实现之间的耦合比较松散,通过接口使用认证服务的客户端对于接口的确切实现来说基本上是不关心的,对于所使用的实现做任何变更应该不需要对客户端做自身任何修改,同样,把一个认证实现替换成另一个,就仅仅是实例化正确实现的问题,这样,不需要对独立的客户端作什么修改就可以把认证的实现换成另一个。
然而这样一种结构仍然需要每个客户端都有内嵌的代码来调用API,所有需要认证的模块中都会需要这些调用,而且是和它们各自的核心逻辑混合在一起。
图1.5显示了一个银行系统如何使用通常的技术来实现日志记录。
尽管使用了一个设计得很好的日志模块,通过提供抽象接口隐藏了格式化和流输出日志消息的细节,然而每一个模块,无论是帐务模块,ATM模块还是数据库模块,还是全都需要调用日志API的代码,总体效果上说,在需要日志功能的模块和日志模块自身之间产生了不必要纠缠。
每一个耦合在图上用一个灰色的箭头表示。
这里就是AOP出场的地方了。
使用AOP,没有一个核心模块需要包含调用日志功能的代码,它们甚至不知道系统中有日志的存在。
图1.6显示的是用AOP实现的与图1.5中相同的日志功能。
现在日志逻辑聚集在日志模块和日志aspect(方面)中,客户端不再包含任何日志代码。
现在横切性的日志需求被映射为仅仅一个模块——日志方面。
有这样的模块化,横切性的日志需求的任何变化只会影响到这个日志方面,而客户端被完全隔离了。
现在暂时不要去管AOP是如何做到这一点的,这将会在第1.6节中解释。
模块化横切性需求很重要,因而有数种技术在实现它。
例如,EnterpriseJavaBeans(EJB)简化了分布式服务器端应用的创建,它处理了一些横切性的关注点,例如安全保密,系统管理,性能,以及容器管理的持久性(CMP)。
让我们来看看持久性这个横切关注点的EJB实现,组件开发人员专注于业务逻辑,而部署开发人员专注于部署的问题,例如把组件的数据影射到数据库里面。
组件开发人员一般来说是不用关心存储问题的。
EJB框架做到了持久关注点和业务逻辑之间的分离,使用的是一个部署描述文件,这是一个XML文件,指定了组件的域如何映射到数据库的列。
类似地,这个框架通过管理部署描述文件中的说明,也分离了用户认证和事务管理等其他的横切关注点。
另一个操纵横切关注点的技术是动态代理(dynamicproxies),它提供了模块化代理这一设计模式的语言级支持。
动态代理很复杂,超出了本书的范围,但无论如何,Java中这个自JDK1.3以来就可用的新特性,提供了模块化横切关注点的一个合理的解决方案,只要关注点够简单就行。
4、AOPBenchmark(基准)
性能测试的AOP或代理框架
(1)字节码框架
AspectWerkz1.0
AspectWerkz2.x
AspectJ1.2
JBossAOP1.0
(2)代理框架
SpringAOP1.1.1
cglibproxy2.0.2
dynaop1.0beta
(3)AspectWerkz可扩展Aspect容器
AspectJ
AOPAlliance
SpringAOP
性能测试结果
5、AOP初步研究
最近看了一些关于AOP(AspectOrientedProgramming)的东西,感觉不错,偶有所得。
AOP其实是定义一种自己的语法规范,然后使用一个源代码处理器,根据这种语法将代码重新展开,感觉就像C,C++语句的宏、模板类,但是更加高级,可以在代码的适当位置(例如符合某种条件的代码片断之前、或者之后),加入自己的代码。
这种做法的好处就是把原来紧密耦合的代码可以分割成独立的模块,最典型的例子就是日志。
这个技术会对于程序设计、系统设计很有好处,使得模块的划分更加灵活。
这种技术正在起步之中,会有很大的发展空间,建议大家共同学习,共同研究一下。
6、面向方面编程是成熟的编程范型,它可以应用于广泛的编程场景,包括遗留应用程序的理解和维护。
请注意,本文假设您大致熟悉AspectJ之下的AOP,特别是AspectJ的静态和动态横切技术。
总体概述
基于Java的AOP使用了灵活而丰富的表达语言,您可以使用它以近乎无限种方式来分解复杂的应用程序。
基于Java的AOP的语法类似于Java语言,您应该很容易就会掌握它。
一旦掌握,AOP就是一种具有许多应用的编程技术。
除了理解遗留系统内部细节外,您还可以使用AOP来非强制性地重构和增强这样的系统。
虽然本文将完全使用AspectJ,不过这里讨论的大多数技术都可移植到其他流行的基于Java的AOP实现,比如AspectWerkz和JBossAOP。
关于横切
任何应用程序都由多个功能性和系统性关注点(concern)组成。
功能性关注点与应用程序的日常使用相关,而系统性关注点则与系统的整体健康和维护相关。
例如,一个银行应用程序的功能性关注点包括账户维护和允许借/贷操作,它的系统性关注点包括安全、事务、性能和审计日志记录。
即使使用最好的编程方法学来开发应用程序,您最终也会发现它的功能性和系统性关注点会以跨越多个应用程序模块的形式相互混杂在一起。
横切是一种AOP技术,用于确保独立的关注点保持模块化,同时仍然足够灵活地在整个应用程序中的不同点应用。
横切包括静态和动态两种类别。
动态横切体现为通过在感兴趣的特定点织入(weavein)新的行为来改变对象的执行行为。
静态横切允许我们通过注入(injectin)附加的方法和/或属性来直接改变对象的结构。
静态横切的语法与动态横切很不相同。
以下术语适用于动态横切:
⏹连接点(joinpoint)是Java程序中的某个特定执行点,比如某个类中的一个方法。
⏹切入点(pointcut)是特定于语言的结构,它表示或捕捉某个特定的连接点。
⏹通知(advice)是在到达某个特定的切入点时要执行的一段代码(通常是一个横切功能)。
⏹方面(aspect)是定义切入点和通知以及它们之间的映射的一个结构。
方面由AOP编译器用来在现有对象中的特定执行点织入附加功能。
本文中的所有代码演示都将利用动态横切。
请参阅参考资料,获得关于静态横切的更多信息。
AspectJ之下的AOP
为了学习本文中的例子,您应该熟悉以下特定于AspectJ之下的AOP的特性。
⏹AspectJ提供一个名为ajc的编译器/字节代码织入器,它编译AspectJ和Java语言文件。
ajc根据需要将方面交织在一起,以产生与任何Java虚拟机(1.1或更高版本)相容的.class文件。
⏹AspectJ支持如下这样的方面,即这些方面规定某个特定的连接点应该永远不会到达。
如果ajc进程判断出情况不是这样,它将发出一个编译时警告或错误(具体取决于该方面)。
应用程序和系统分析
在下面几节中,您将学习两种使用AOP的不同的应用程序和系统分析机制。
第一种机制我称之为静态分析,它要求您做以下事情:
⏹从CVS(或您所使用的其他任何代码版本控制系统)中把整个应用程序代码库签出到本地区域中。
⏹修改生成文件(buildfile)以使其使用AspectJ编译器(ajc)。
⏹在适当的位置包括方面(aspect)类。
⏹执行一次完整的系统生成过程。
第二种机制我称之为动态分析,它要求您不仅在本地区域生成(build)系统,而且要运行该系统的具体用例(usecase),同时将运行时反射中的信息收集到运行系统中。
在下面几节中,我将详细讨论每种机制,同时使用代码例子来说明关键概念。
织入过程可以在编译时进行(使用AspectJ),或者在加载时进行(使用AspectWerkz)。
在设计方面时,谨记AOP实现可能具有关于环境公开(contextexposure)的固有限制(例如,AspectJ不允许通过执行环境向通知代码公开方法的本地变量)是很重要的。
然而一般来讲,使用方面来定制系统将带来更清洁的实现,它使得隔离的关注点与系统代码的基线版本更清楚地分离。
7、什么是横切?
(重要)
横切是面向方面编程的专有名词。
它指的是在一个给定的编程模型中穿越既定的职责部分(比如日志记录和性能优化)的操作。
在横切的世界里,横切有两种类型:
动态横切和静态横切。
在本文中,尽管我将简要地同时讨论二者,但我主要关注静态横切。
动态横切
动态横切是通过切入点和连接点在一个方面中创建行为的过程,连接点可以在执行时横向地应用于现有对象。
动态横切通常用于帮助向对象层次中的各种方法添加日志记录或身份认证。
下面让我们花点时间了解一下动态横切中的一些实际概念:
⏹方面(aspect)类似于Java编程语言中的类。
方面定义切入点和通知(advice),并由诸如AspectJ这样的方面编译器来编译,以便将横切(包括动态的和静态的)织入(interweave)现有的对象中。
⏹一个连接点(joinpoint)是程序执行中一个精确执行点,比如类中的一个方法。
例如,对象Foo中的方法bar()就可以是一个连接点。
连接点是个抽象的概念;不用主动定义一个连接点。
⏹一个切入点(pointcut)本质上一个用于捕捉连接点的结构。
例如,可以定义一个切入点来捕捉对对象Foo中的方法bar()的所有调用。
和连接点相反,切入点需要在方面中定义。
⏹通知(advice)是切入点的可执行代码。
一个经常定义的通知是添加日志记录功能,其中切入点捕捉对对象Foo中的bar()的每个调用,然后该通知动态地插入一些日志记录功能,比如捕捉bar()的参数。
这些概念是动态横切的核心,虽然正如我们即将看到的,它们并不全都是静态横切所必需的。
请参阅参考资料来了解关于动态横切的更多内容。
静态横切
静态横切和动态横切的区别在于它不修改一个给定对象的执行行为。
相反,它允许通过引入附加的方法字段和属性来修改对象的结构。
此外,静态横切可以把扩展和实现附加到对象的基本结构中。
虽然现在还无法谈及静态横切的普遍使用——它看起来是AOP的一个相对未被探索(尽管非常具有吸引力)的特性——然而这一技术蕴含的潜力是巨大的。
使用静态横切,架构师和设计者能用一种真正面向对象的方法有效地建立复杂系统的模型。
静态横切允许您不用创建很深的层次结构,以一种本质上更优雅、更逼真于现实结构的方式,插入跨越整个系统的公共行为。
在本文剩下的篇幅中,我将重点讲解静态横切的技术和应用。
创建静态横切
创建静态横切的语法和动态横切有很大的不同,即没有切入点和通知。
给定一个对象(比如下面定义的Foo),静态横切使得创建新方法、添加附加的构造函数,甚至改变继承层次都变得十分简单。
我们将用一个例子来更好地展示静态横切是怎样在一个现有的类中实现的。
清单1显示了一个简单的没有方面的Foo。
清单1.没有方面的Foo
publicclassFoo{
publicFoo(){
super();
}
}
如清单2所示,在一个对象中添加一个新的方法和在一个方面中定义一个方法是同样简单的。
清单2.向Foo添加一个新方法
publicaspectFooBar{
voidFoo.bar(){
System.out.println("inFoo.bar()");
}
}
构造函数略有区别的地方在于new关键字是必需的,如清单3所示。
清单3.向Foo添加一个新的构造函数
publicaspectFooNew{
publicFoo.new(Stringparm1){
super();
System.out.println("inFoo(stringparm1)");
}
}
改变对象的继承层次需要一个declareparents标签。
比如,为了变成多线程的,Foo将需要实现Runnable,或者扩展Thread。
清单4显示了用declareparents标签来改变Foo的继承层次。
清单4.改变Foo的继承层次
publicaspectFooRunnable{
declareparents:
FooimplementsRunnable;
publicvoidFoo.run(){
System.out.println("inFoo.run()");
}
}
现在,您可能开始独自设想静态横切的含意了,特别是在与创建松散耦合、高度可扩展的系统有关时。
在下面的几小节中,我将带您看一个一个真实的设计和实现场景,以展示使用静态横切来扩展您的企业应用的灵活性是多么容易。
实现场景
企业系统经常被设计来利用第三方的产品和库。
为了不把整个结构和所需产品耦合在一起,通常在设计来与外部厂商代码交互的应用中包括进一个抽象层。
在插
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- AOP 学习 笔记