AOP知识整理html
AOP(Aspect-Oriented Programming):面向切面的编程。OOP(Object-Oriented Programming)面向对象的编程。对于OOP咱们已经再熟悉不过了,对于AOP,可能咱们会以为是一种新特性,其实AOP是对OOP的一种补充,OOP面向的是纵向编程,继承、封装、多态是其三大特性,而AOP是面向横向的编程。spring
面向切面编程(AOP)经过提供另一种思考程序结构的途经来弥补面向对象编程(OOP)的不足。在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面。切面能对关注点进行模块化,例如横切多个类型和对象的事务管理。编程
AOP框架是Spring的一个重要组成部分。可是Spring IoC容器并不依赖于AOP,这意味着你有权利选择是否使用AOP,AOP作为Spring IoC容器的一个补充,使它成为一个强大的中间件解决方案。数组
AOP在Spring Framework中的做用缓存
提供声明式企业服务,特别是为了替代EJB声明式服务。最重要的服务是声明性事务管理(这个我想是AOP使用最多的一处了)。框架
容许用户实现自定义切面,用AOP来完善OOP的使用。模块化
1.AOP概念:学习
学习AOP,固然得先了解一下其众多的概念性术语:优化
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可使用基于模式)或者基于@Aspect注解的方式来实现。spa
链接点(Joinpoint):在程序执行过程当中某个特定的点,好比某方法调用的时候或者处理异常的时候。在Spring AOP中,一个链接点老是表示一个方法的执行。
通知(Advice):在切面的某个特定的链接点上执行的动做。其中包括了“around”、“before”和“after”等不一样类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器作通知模型,并维护一个以链接点为中心的拦截器链。
切入点(Pointcut):匹配链接点的断言。通知和一个切入点表达式关联,并在知足这个切入点的链接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和链接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为链接类型声明(inter-type declaration))。Spring容许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可使用引入来使一个bean实现IsModified
接口,以便简化缓存机制。
目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。 既然Spring AOP是经过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
AOP代理(AOP Proxy):AOP框架建立的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理能够是JDK动态代理或者CGLIB代理。
织入(Weaving):把切面链接到其它的应用程序类型或者对象上,并建立一个被通知的对象。这些能够在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其余纯Java AOP框架同样,在运行时完成织入。
通知类型:
前置通知(Before advice):在某链接点以前执行的通知,但这个通知不能阻止链接点以前的执行流程(除非它抛出一个异常)。
后置通知(After returning advice):在某链接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
最终通知(After (finally) advice):当某链接点退出的时候执行的通知(不管是正常返回仍是异常退出)。
环绕通知(Around Advice):包围一个链接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知能够在方法调用先后完成自定义的行为。它也会选择是否继续执行链接点或直接返回它本身的返回值或抛出异常来结束执行。
环绕通知是最经常使用的通知类型。和AspectJ同样,Spring提供全部类型的通知,咱们推荐你使用尽量简单的通知类型来实现须要的功能。例如,若是你只是须要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成一样的事情。用最合适的通知类型可使得编程模型变得简单,而且可以避免不少潜在的错误。好比,你不须要在JoinPoint
上调用用于环绕通知的proceed()
方法,就不会有调用的问题。
在这里,基于@AspectJ的AOP我就很少写了,由于我更青睐于Spring中使用ProxyFactoryBean建立AOP代理。
2.使用ProxyFactoryBean建立AOP代理:
在Spring里建立一个AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。 这个类对应用的切入点和通知提供了完整的控制能力(包括它们的应用顺序)。像其它的FactoryBean
实现同样,ProxyFactoryBean
引入了一个间接层。若是你定义一个名为foo
的ProxyFactoryBean
, 引用foo
的对象看到的将不是ProxyFactoryBean
实例自己,而是一个ProxyFactoryBean
实现里getObject()
方法所建立的对象。 这个方法将建立一个AOP代理,它包装了一个目标对象。
ProxyFactoryBean
类自己也是一个JavaBean,其属性主要有以下用途:
指定你但愿代理的目标对象
指定是否使用CGLIB。
一些主要属性从org.springframework.aop.framework.ProxyConfig
里继承下来(这个类是Spring里全部AOP代理工厂的父类)。这些主要属性包括:
proxyTargetClass
:这个属性为true
时,目标类自己被代理而不是目标类的接口。若是这个属性值被设为true
,CGLIB代理将被建立。
optimize
:用来控制经过CGLIB建立的代理是否使用激进的优化策略。 除非彻底了解AOP代理如何处理优化,不然不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理; 对于JDK动态代理(缺省代理)无效。
frozen
:若是一个代理配置是frozen
的,就不容许对该配置进行修改。 这在简单优化和不但愿调用者在代理建立后操做代理(经过Advised
接口) 时颇有用。缺省值为false
,便可以进行相似添加附加通知的操做。
exposeProxy
:决定当前代理是否被暴露在一个ThreadLocal
中以便被目标对象访问。若是目标对象须要获取代理并且exposeProxy
属性被设为true
,目标对象可使用AopContext.currentProxy()
方法。
aopProxyFactory
:使用AopProxyFactory
的实现。这提供了一种方法来自定义是否使用动态代理,CGLIB或其它代理策略。 缺省实现将根据状况选择动态代理或者CGLIB。通常状况下应该没有使用这个属性的须要;它是被设计来在Spring 1.1中添加新的代理类型的。
ProxyFactoryBean
中须要说明的其它属性包括:
proxyInterfaces
:须要代理的接口名的字符串数组。 若是没有提供,将为目标类使用一个CGLIB代理。
interceptorNames
:Advisor
的字符串数组,能够包括拦截器或其它通知的名字。 顺序是很重要的,排在前面的将被优先服务。就是说列表里的第一个拦截器将可以第一个拦截调用。
这里的名字是当前工厂中bean的名字,包括父工厂中bean的名字。这里你不能使用bean的引用由于这会致使ProxyFactoryBean
忽略通知的单例设置。
你能够把一个拦截器的名字加上一个星号做为后缀(*
)。这将致使这个应用程序里全部名字以星号以前部分开头的通知器都被应用。
单例:工厂是否应该返回同一个对象,不论方法getObject()
被调用的多频繁。 多个FactoryBean
实现都提供了这个方法。缺省值是true
。 若是你但愿使用有状态的通知--例如,有状态的mixin--能够把单例属性的值设置为false
来使用原型通知。
3.基于JDK和CGLIB的代理:
若是一个须要被代理的目标对象的类(后面将简单地称它为目标类)没有实现任何接口,那么一个基于CGLIB的代理将被建立。 这是最简单的场景,由于JDK代理是基于接口的,没有接口意味着没有使用JDK进行代理的可能.
若是ProxyFactoryBean
的proxyTargetClass
属性被设为true
,那么一个基于CGLIB的代理将建立。 这样的规定是有意义的,遵循了最小惊讶法则(保证了设定的一致性)。甚至当ProxyFactoryBean
的proxyInterfaces
属性被设置为一个或者多个全限定接口名, 而proxyTargetClass
属性被设置为true
仍然将实际使用基于CGLIB的代理。
若是ProxyFactoryBean
的proxyInterfaces
属性被设置为一个或者多个全限定接口名,一个基于JDK的代理将被建立。 被建立的代理将实现全部在proxyInterfaces
属性里被说明的接口; 若是目标类实现了所有在proxyInterfaces
属性里说明的接口以及一些额外接口,返回的代理将只实现说明的接口而不会实现那些额外接口。
若是ProxyFactoryBean
的proxyInterfaces
属性没有被设置, 可是目标类实现了一个(或者更多)接口,那么ProxyFactoryBean
将自动检测到这个目标类已经实现了至少一个接口, 一个基于JDK的代理将被建立。被实际代理的接口将是目标类所实现的所有接口; 实际上,这和在proxyInterfaces
属性中列出目标类实现的每一个接口的状况是同样的。 然而,这将显著地减小工做量以及输入错误的可能性。