1、Spring AOP 技术概述第 1 页(共 5 页)1 什么是 AOP在介绍 Spring AOP 之前,先简单介绍一下 AOP 的概念。AOP 是(Aspect Oriented Programming)的缩写,意思是面向切面(方面)编程。是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的技术。AOP 可以说是 OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无
2、关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。AOP 技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为” Aspect”, 即方面(切面) 。所谓“方面” ,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP 代表的是一个横向的关系,如果说“对象
3、”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。如 果 说 面 向 对 象 编 程 是 关 注 将 需 求 功 能 划 分 为 不 同 的 并 且 相 对 独 立 、 封 装 良 好 的 类 , 并 让它 们 有 着 属 于 自 己 的 行 为 , 依 靠 继 承 和 多 态 等 来 定 义 彼 此 的 关 系 的 话 ; 那 么 面 向 方 面 编 程 则是 希 望 能 够 将 通 用 需 求 功 能 从 不 相
4、关 的 类 当 中 分 离 出 来 , 能 够 使 得 很 多 类 共 享 一 个 行 为 , 一 旦这 些 功 能 发 生 变 化 , 不 必 修 改 很 多 类 , 而 只 需 要 修 改 这 个 行 为 即 可 。1.1 AOP中的一些基本概念方面(Aspect):对横向分布在多个对象中的关注点所做的模块化。在企业应用中,事务管理就是一个典型的横切关注点。连接点(Joinpoint ):程序执行过程中的一个点,例如对某个方法的调用或者某个特定异常的抛出都可以称为连接点。 通知(Advice):AOP 框架在某个连接点所采取的行为。通知有多种类型,包括“环绕”通知, “前置 ”通知和 “异
5、常”通知等,后文将对通知类型进行讨论。 切入点(Pointcut ):指通知的应用条件,用于确定某个通知要被应用到哪些连接点上。AOP框架应允许让开发人员指定切入点。 引入(Introduction):向目标对象添加方法或字段的行为。目标对象(Target object):指包含连接点的对象。也称为被通知或被代理对象。AOP 代理( AOP proxy):由 AOP 框架在将通知应用于目标对象后创建的对象。 织入(Weaving ):将方面进行组装,以创建一个目标对象。织入可以在编译期完成,也可以在运行时完成。横切技术:“横切”是 AOP 的专有名词。它是一种蕴含强大力量的相对简单的设计和编程
6、技术,尤其是用于建立松散耦合的、可扩展的企业系统时。横切技术可以使得 AOP 在一个给定的编程模型中穿越既定的职责部分(比如日志记录和性能优化)的操作。横切关注点:一个关注点(concern)就是一个特定的目的,一块我们感兴趣的区域,一段我们需要的逻辑行为。从技术的角度来说,一个典型的软件系统包含一些核心的关注点和系统级的关注点。举个例子来说,一个信用卡处理系统的核心关注点是借贷/存入处理,而系统级的关注点则是日志、事务完整性、授权、安全及性能问题等,许多关注点即横切关注点(crosscutting concerns)会在多个模块中出现。如果使用现有的编程方法,横切关注点会横越多个模块,结果是
7、使系统难以设计、理解、实现和演进。AOP 能够比上述方法更好地分离系统关注点,从而提供模块化的横切关注点。Spring AOP 技术概述第 2 页(共 5 页)1.2 AOP的通知类型环绕通知(Around Advise):包围(按:即在连接点执行的前、后执行)某个连接点(如方法调用)的通知。这是功能最强大的一种通知。环绕通知允许在方法调用的前后执行自定义行为。它可以决定是让连接点继续执行,还是用自己的返回值或异常来将连接点“短路” 。 前置通知(Before Advise):在某个连接点执行之前执行,但是不具备阻止连接点继续执行的能力(除非它抛出异常) 。 异常通知(Throws Advis
8、e):当方法(连接点)抛出异常时执行。后置通知(After returning Advise):在连接点正常执行完成后执行,例如,如果方法正常返回,没有抛出异常时,后置通知就会被执行。1.3 AOP的实现方式采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;采用静态织入的方式,引入特定的语法创建“切面”,从而使得编译器可以在编译期间织入有关“切面”的代码2 Spring AOP 技术2.1 Spring框架的特点Spring 框架目前如此流行,一方面的原因在于 Spring 提供了一套全面并且十分成熟的轻型应用程序基本框架,并且对复杂的应用开发提供了有力的支持。
9、除此之外,从实际应用开发角度来看,Spring 最大的优势在于它是从实际项目开发经验中抽取的,其提供了丰富的类库,可大大节省编码量,它是一种高效的、可高度重用的应用框架。Spring 框架中目前最吸引人也是该应用框架最具特色的地方就是名为控制反转(IOC=Inverse Of Control)或者依赖注入(DI=Dependence Injection)的设计思想,这是一种相当优秀的设计思想,即“HollyWood”原则:“Dont call me ,I will call you!”。但是,仅仅凭借着这样一个单纯的设计模式并不能使得 Spring 如此成功,Spring 最成功的地方,还是目
10、前使用最为广泛的 AOP 应用,也就是 Spring 中基于 AOP 实现的业务管理机制,也正是由于这一点,使得 Spring AOP 成为应用框架中极其闪光的一个亮点。2.2 AOP思想在Spring框架中的体现2.2.1 事务管理对于 J2EE 应用程序而言,事务的处理一般有两种模式:依赖特定事务资源的事务处理与依赖容器的参数化事务管理。在这里我们略去对第一种处理方式的说明,直接对第二种方式,即依赖容器的参数化事务管理来阐述笔者的观点。对于传统的基于事务资源的事务处理而言,Spring 并不会产生什么影响,我们照样可以成功编写并且运行这样的代码。对于依赖容器的参数化事务管理而言,Sprin
11、g 则可以用来帮助实现对事务的管理。Spring 属于轻量级容器,通过使用 AOP 来提供声明式事务管理,即可通过 Spring 实现基于容器的事务管理(从本质上来讲,Spring 的事务管理是基于动态 AOP) 。2.2.2 动态代理机制Spring 框架中所提供的 AOP 支持,是基于动态 AOP 机制实现的,即通过动态 Proxy 模式,在目标对象的方法调用前后插入相应的处理代码。AOP 代理可以是基于 JDK 动态代理,也可以是基Spring AOP 技术概述第 3 页(共 5 页)于 CGLIB 代理。Spring 默认使用的是基于 Java Dynamic Proxy 模式实现,这
12、样任何的接口都能被代理。基于 Spring 框架的应用程序开发,程序员会有一种自然的倾向性来实现面向接口编程而不是类,业务对象通常也是实现一个或者多个接口,这也是一种良好的编程习惯。Spring 也可以基于CGLIB 实现 AOP 代理,这样所代理的是类而不是接口。如果一个业务对象没有实现某一个接口,那么 CGLIB 将被使用。2.3 Spring AOP动态代理解析2.3.1 动态代理简单实现(1) 编写要代理的接口(2) 把目标对象去实现接口(3) 开发相应的通知(前置通知 ,后置通知,环绕通知,异常通知) ,写出代码(4) 在 spring 容器文件配置 配置通知类 配置目的对象 配置代
13、理对象(三个内容 接口, 通知织入, 代理哪些对象 )(5) 测试2.4 spring的AOP机制实现类代理的方式2.4.1 概述一种是单个代理,一种是自动代理。 单个代理通过 ProxyFactoryBean 来实现,自动代理通过 BeanNameAutoProxyCreator 或者 DefaultAdvisorAutoProxyCreator 实现。 为了给程序加入日志功能,我们采用 aop 的方式。编写一个日志通知类 :com.zz.service.server.LogService 配置文件中添加以下代码: .*insertNadCustomerRealFinance.*2.4.2
14、单个代理(这种方式费时费力,项目中需要多出加入通知的话,不会采用此方式)需要在 Spring 配置文件中添加: logAdvisorSpring AOP 技术概述第 4 页(共 5 页) 项目中其他通过注入方式设置 FinanceInFacade 属性的 bean,不能直接注入 FinanceInFacade 的 bean id.而要注入代理的 bean id,此例中就是 log。 2.4.3 自动代理增加此行,容器会自动根据通知要匹配的切入点,为包含切入点的类创建代理动态。2.5 Spring AOP解析 2.5.1 创建通知 为实现 AOP,开发者需要开发 AOP 通知(Advice)。A
15、OP 通知(Advice) 包含了方面(Aspect )的逻辑。当创建一个 Advice 对象的时候,你就编写了实现横切(cross-cutting)功能。 Spring 的连接点是用方法拦截器实现的,这就意味着你编写的 Spring AOP 通知将在方法调用的不同点组入进程序中。由于在调用一个方法时有几个不同的时间点,Spring 可以在不同的时间点组入进程序。Spring AOP 中,提供了四种通知的接口: MethodBeforeAdvice 用于在目标方法调用前触发;AfterReturningAdvice 用于在目标方法调用后触发; ThrowsAdvice 用于在目标方法抛出异常时
16、触发;MethodInterceptor 用于实现 Around 通知(Advice ) ,在目方法执行的前后触发。 如果要实现相应功能,则需要实现上述对应的接口。例如:实现 Before 通知(Advice )需要实现方法 void before(Method method, Object args, Object target) ,实现 After 通知(Advice) 需要实现方法 void afterReturning (Method method, Object args, Object target)。 2.5.2 在 Spring 中定义切入点 在不能明确调用方法的时候,通知就很
17、不实用。切入点则可以决定特定的类,特定的方法是否匹配特定标准。如果某匹配,则通知将应用到此方法上。Spring 切入点允许用很灵活的方式将通知组织进我们的类中。Spring 中的切入点框架的核心是 Pointcut 接口,此接口允许我们定义组入通知中的类和方法。许多方面是通过一系列的通知和切入点组合来定义。 在 Spring 中,一个 advisor 就是一个方面的完整的模块化表示。Spring 提供了 PointcutAdvisor接口把通知和切入点组合成一个对象。Spring 中很多内建的切入点都有对应的 PointcutAdvisor,这使得你可以很方便在一个地方管理切入点和通知。Spr
18、ing 中的切入点分为两类:静态和动态。因为静态切入点的性能要优于动态切入点,所以优先考虑使用。Spring 为我们提供创建静态切入点很实用的类 StaticMethodMatherPointcut。在这个类中,我们只需要关心 setMappedName 和setMappedNams 方法。你可以使用具体的类名,也可以使用通配符。如:设置 mappedName 属性为set* 则匹配所有的 set 方法。Spring 还提供了另通过正则表达式来创建静态切入点的实用类RegexpMethodPointcut。通过使用 Perl 样式的正则表达式来定义你感兴趣的方法。当切入点需要运行时参数值来执行
19、通知时,这时就需要使用动态切入点。Spring 提供了一个内建的动态切入点:ControlFlowPointcut,此切入点匹配基于当前线程的调用堆栈。我们可以在只有在当前线程执行的执行时找到特定的类和特定的方法才返回 true。使用动态切入点有很大的性能损耗。大多数的切入点可以静态确定,我们很少有机会创建动态切入点。为了增加可切入点的可重用性,Spring 提Spring AOP 技术概述第 5 页(共 5 页)供了切入点上的集合操作交集和合并。 2.5.3 用 ProxyFactoryBean 创建 AOP 代理 ProxyFactoryBean,和其他 Spring 的 FactoryB
20、ean 实现一样,引入一个间接的层次。如果你定义一个名字为 myfactory 的 ProxyFactoryBean, 引用 myfactory 的对象所看到的不是ProxyFactoryBean 实例本身,而是由实现 ProxyFactoryBean 的类的 getObject()方法所创建的对象。这个方法将创建一个包装了目标对象 的 AOP 代理。使用 ProxyFactoryBean 或者其他 IOC 可知的类来创建 AOP 代理的最重要的优点之一是 IOC 可以管理通知和切入点。这是一个非常的强大的功能,能够实现其他 AOP 框架很难实现的特定的方法。例如,一个通知本身可以引用应用对象
21、(除了目标对象, 它在任何 AOP 框架中都可以引用应用对象) ,这完全得益于依赖注入所提供的可插入性。通常,我们不需要 ProxyFactoryBean 的全部功能,因为我们常常只对一个方面感兴趣: 例如,事务管理。当我们仅仅对一个特定的方面感兴趣时,我们可以使用许多便利的工厂来创建AOP 代理,如:TransactionProxyFactoryBean。 2.5.4 自动代理的使用 在应用较小时,只有很少类需要被通知的时,ProxyFactoryBean 可以很好的工作。当有许多类需要通知的时,显示的创建每个代理就显得很繁琐。幸运的是 Spring 提供了是使用自动通过容器来创建代理。这时
22、,就只需要配置一个 Bean 来做繁琐的工作。Spring 提供了两个类实现自动代理:BeanNameAutoProxyCreator 和 DefaultAdvisorAutoProxyCreator。 BeanNameAutoProxyCreator 为匹配名字的 Bean 产生代理,它可以使用在将一个或者多个方面应用在命名相似的 Bean 中。自动代理框架假设代理将要暴露出什么接口。如果目标 Bean 没有实现任何接口,这时就会动态产生一个子类。而更强大的自动代理是 DefaultAdvisorAutoProxyCreator,你所需要做的是在 BeanFactory 中包含它的配置。这个
23、类的奇妙之处在于他使用实现了BeanPostProcessor 接口。在 Bean 定义被加载倒 Spring 容器中后,DefaultAdvisorAutoProxyCreator将搜索上下文中的 Advisor,最后它将 Advisor 应用到匹配 Advisor 切入点的 Bean 中。这个代理只对 Advisor 起作用,它需要通过 Advisor 来得到需要通知的 Bean。元数据自动代理(MetaData AutoProxy) 。元数据自动代理配置依赖于源代码属性而不是外部 XML 配置文件。这可以非常方便的使源代码和 AOP 元数据组织在同一个地方。元数据自动代理最常用的地方是用来声明事务。Spring 提供了很强的框架来通过 AOP 框架来声明事务。