这篇文章主要介绍了 Java AOP 知识详细介绍的相关资料, 需要的朋友可以参考下
Java AOP
AOP 知识整理
AOP(Aspect-Oriented Programming): 面向切面的编程 OOP(Object-Oriented Programming)面向对象的编程对于 OOP 我们已经再熟悉不过了, 对于 AOP, 可能我们会觉得是一种新特性, 其实 AOP 是对 OOP 的一种补充, OOP 面向的是纵向编程, 继承封装多态是其三大特性, 而 AOP 是面向横向的编程
面向切面编程 (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 注解的方式来实现
连接点(Joinpoint): 在程序执行过程中某个特定的点, 比如某方法调用的时候或者处理异常的时候在 Spring AOP 中, 一个连接点总是表示一个方法的执行
通知 (Advice): 在切面的某个特定的连接点上执行的动作其中包括了 aroundbefore 和 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 属性中列出目标类实现的每个接口的情况是一样的 然而, 这将显著地减少工作量以及输入错误的可能性
来源: http://www.phperz.com/article/18/0223/358785.html