4Jan.2009©NeusoftConfidentialCopyright2008ByNeusoftGroup.Allrightsreserved第四章面向切面编程(AOP)Copyright2009ByNeusoftGroup.Allrightsreserved4Jan.2009Confidential简介–Spring的一个关键的组件就是AOP框架。尽管如此,SpringIoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得SpringIoC容器更加完善。–SpringAOP的出现是为了取代EJB中的事务机制,它有这种声明式的事务机制,其实AOP的这种思想早就已经有了,并不是一种什么新的技术,也并不是说专门由java这里来实现的。–面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。4Jan.2009Confidential简介–AOP最重要的一点,就是提供了声明式服务,好在哪里呢?使得我们不用修改代码,就具有了这种能力,EJB的事务管理只能用在EJB组件上,而spring提供的这种功能,能够对普通java对象提供这种声明式事务管理服务,而且这种普通java对象pojo,是不实现任何接口的,即,我们不用实现Spring的任何接口,或是继承spring的任何类,就拥有了这种能力,让我们的对象在透明的情况下、不知道的情况下拥有了这样的能力。4Jan.2009ConfidentialAOP涉及到的概念•切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在SpringAOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@Aspect注解(@AspectJ风格)来实现。•连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在SpringAOP中,一个连接点总是代表一个方法的执行。通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。•通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。4Jan.2009ConfidentialAOP涉及到的概念•切入点(Pointcut):匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。•引入(Introduction):(也被称为内部类型声明(inter-typedeclaration))。声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现IsModified接口,以便简化缓存机制。•目标对象(TargetObject):被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做被通知(advised)对象。既然SpringAOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。4Jan.2009ConfidentialAOP涉及到的概念•AOP代理(AOPProxy):AOP框架创建的对象,用来实现切面契约(aspectcontract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。注意:Spring2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。•织入(Weaving):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯JavaAOP框架一样,在运行时完成织入。•通知的类型:•前置通知(Beforeadvice):在某连接点(joinpoint)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。4Jan.2009ConfidentialAOP涉及到的概念•返回后通知(Afterreturningadvice):在某连接点(joinpoint)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。•抛出异常后通知(Afterthrowingadvice):在方法抛出异常退出时执行的通知。•后通知(After(finally)advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。•环绕通知(AroundAdvice):包围一个连接点(joinpoint)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。4Jan.2009ConfidentialAOP的功能和目标•SpringAOP用纯Java实现。它不需要专门的编译过程。•SpringAOP不需要控制类装载器层次,因此它适用于J2EEweb容器或应用服务器。•Spring目前仅支持使用方法调用作为连接点(joinpoint)(在Springbean上通知方法的执行)。虽然可以在不影响到SpringAOP核心API的情况下加入对成员变量拦截器支持,但Spring并没有实现成员变量拦截器。如果你需要把对成员变量的访问和更新也作为通知的连接点,可以考虑其它语法的Java语言,例如AspectJ。•Spring实现AOP的方法跟其他的框架不同。Spring并不是要尝试提供最完整的AOP实现(尽管SpringAOP有这个能力),相反的,它其实侧重于提供一种AOP实现和SpringIoC容器的整合,用于帮助解决在企业级开发中的常见问题。4Jan.2009ConfidentialAOP的功能和目标•因此,SpringAOP通常都和SpringIoC容器一起使用。Aspect使用普通的bean定义语法(尽管Spring提供了强大的“自动代理(autoproxying)”功能):与其他AOP实现相比这是一个显著的区别。有些事使用SpringAOP是无法轻松或者高效的完成的,比如说通知一个细粒度的对象。这种时候,使用AspectJ是最好的选择。不过经验告诉我们:于大多数在J2EE应用中遇到的问题,只要适合AOP来解决的,SpringAOP都没有问题,SpringAOP提供了一个非常好的解决方案。•SpringAOP从来没有打算通过提供一种全面的AOP解决方案来取代AspectJ。我们相信无论是基于代理(proxy-based)的框架比如说Spring亦或是full-blown的框架比如说是AspectJ都是很有价值的,他们之间的关系应该是互补而不是竞争的关系。Spring2.0可以无缝的整合SpringAOP,IoC和AspectJ,使得所有的AOP应用完全融入基于Spring的应用体系。这样的集成不会影响SpringAOPAPI或者AOPAllianceAPI;SpringAOP保留了向下兼容性。4Jan.2009ConfidentialAOP代理•Spring缺省使用J2SE动态代理(dynamicproxies)来作为AOP的代理。这样任何接口都可以被代理。•Spring也支持使用CGLIB代理.对于需要代理类而不是代理接口的时候CGLIB代理是很有必要的。如果一个业务对象并没有实现一个接口,默认就会使用CGLIB。作为面向接口编程的最佳实践,业务对象通常都会实现一个或多个接口。但也有可能会强制使用CGLIB,在这种情况(希望不常有)下,你可能需要通知一个没有在接口中声明的方法,或者需要传入一个代理对象给方法作为具体类型。•要很好的理解AOP思想,我们需要循序渐进的阶段性的来理解和学习。4Jan.2009ConfidentialAOP代理-静态代理•接下来,我们用实例来解释代理:当我们写完业务方法之后,需要安全性检查,即它的执行前提式要先进行安全性检查。•代理模式它是结构型的一个模式,它可以控制元对象,即控制元对象是否允许被访问等等。代理的很重要的一点就是它不会改变元对象的接口,因为它只是代理。•代理我们这里可分类为:静态代理、动态代理。–所有的静态代理,代理类是你确确实实能看到的;–而动态的,是在运行期生成的,所以这两种方式我们都需要来领会。•见:spring_static_proxy.project4Jan.2009Confidential•代理模式有个很显著的特征,就是它要跟目标对象的接口是一致的,接口是不能改的。•写一个代理类,它要有一个共同的接口,在代理里面可以控制元对象,也就是我们的目标对象,要控制目标对象就要具有一个目标对象的一个应用,这个应用在本示例里我们可以自己实例化一个UserManagerImpl对象,但是显然这样不够灵活,我们可以在UserManagerImplProxy里提供一个构造函数,让它实例化的时候将UserManagerImpl对象传递进来即可,这样会相对灵活。AOP代理-静态代理4Jan.2009ConfidentialpublicclassUserManagerImplProxyimplementsUserManagerIface{privateUserManagerIfaceuserManager;publicUserManagerImplProxy(UserManagerIfaceuserManager){this.userManager=userManager;}…….}AOP代理-静态代理4Jan.2009Confidential•正如同“安全性检查”这样的动作跟我们的实际业务是没有什么直接关系,它们是两种不同的逻辑。•它是一种不同于我们真正关心的业务的东西,是一种“服务”。•它是独立的事务,双方实际上互不影响。这样的事务存在与否不直接影响我们的业务,这就是横切性的问题。–即:它可以直接切入进来做它该做的事情;也可以抽身而走,不留任何痕迹,对我们的业务代码的正常运行不产生伤筋动骨的影响。•这些散布在很多类里的方法,我们称之为横切性的“关注点”。AOP代理-静态代理4Jan.2009Confidential•以上是我们采用的静态代理,是没办法的办法。•我们必须引入另一种机制,这种机制,它分析问题的方式是不同的,解决问题的手段也不一样,它关注的就是这些“横切的关注点”,把这些横切的关注点(这些安全性检查的方法)单独的提取出来,放到一个类里封装,在运行的时候,自动的把它们“切入”到需要它的地方,这种思想就是AOP。•它关注的是“关注点”,它不关心是什么对象。它关心的是在这些对象中的具有横切性的问题,把它们提取出来,进行模块化,在运行时把他们“织入”进去,即在运行期切入进去。•接下来我们了解下动态代理。AOP代理-动态代理4Jan.2009Confidential•JDK动态代理:–Spring的AOP的默认实现就是采用jdk的动态代理机制实现的。–通过之前的分析,我们要把横切性的关注点单独的提取出来,这就是动态代理的思想。–把散布在程序各个角落的关注点提取出来,进行模块化。–模块化的好处:即模块化之后,我们只要单独维护这个模块就可以了,不用去维护散布在各处的关注点,否则,难度大,效率低。–AOP技术应该是OO的在技术上的补充,见示例:spring_dynamic_proxy.projectAOP代理-动态代