Aspect-Oriented Programming (面相切面编程 AOP) 用另外的一种编程架构的思考来补充 Object-Oriented Programming (面相对象编程OOP)。OOP 主要的模块单元是 class (类),而 AOP 是 aspect(切面)。切面使得诸如事务管理等跨越多个类型和对象的关注点模块化。(这样的关注点在 AOP 的字眼里往往被称为 crosscutting (横切关注点))
AOP 是 Spring 里面的主要的组件。虽然 Spring IoC 容器没有依赖 AOP,意味着你不想用的时候也无需使用 AOP,但 AOP 提供一个非常有用的中间件解决方案来作为 Spring IoC 的补充。
Spring 2.0 AOP
Spring 2.0 引入了一种更加简单并且更强大的方式来自定义切面,用户可以选择使用 [schema-based](9.3. Schema-based AOP support.md)(基于模式)的方式或者使用 [@AspectJ 注解样式](9.2. @AspectJ support.md)。这两种风格都完全支持 Advice(通知)类型和 AspectJ 的切入点语言,虽然实际上仍然使用 Spring AOP 进行 weaving (织入)。
本章主要讨论 Spring 2.0 对基于模式和基于 @AspectJ 的 AOP 支持。 Spring 2.0 完全保留了对 Spring 1.2 的向下兼容性,[下一章 10. Spring AOP APIs](10. Spring AOP APIs.md) 将讨论 Spring 1.2 API 所提供的底层的 AOP 支持。
Spring中所使用的AOP:
如果你只打算使用通用的声明式服务或者预先打包的声明式中间件服务,例如 pooling(缓冲池),你可以不直接使用 AOP ,可以忽略本章大部分内容
###9.1.1 AOP concepts 概念
首先让我们从定义一些重要的 AOP 概念开始。这些术语不是 Spring 特有的。 不幸的是,Spring 术语并不是特别的直观;如果 Spring 使用自己的术语,将会变得更加令人困惑。
通知的类型:
环绕通知是最常用的一种通知类型。跟 AspectJ 一样,Spring 提供所有类型的通知,我们推荐你使用尽量简单的通知类型来实现需要的功能。 例如,如果你只是需要用一个方法的返回值来更新缓存,虽然使用环绕通知也能完成同样的事情, 但是你最好使用 After returning 通知而不是环绕通知。 用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。 比如,你不需要调用 JoinPoint
(用于Around Advice)的 proceed()
方法,就不会有调用的问题。
在Spring 2.0中,所有的通知参数都是静态类型,因此你可以使用合适的类型(例如一个方法执行后的返回值类型)作为通知的参数而不是使用一个对象数组。
pointcut(切入点)和 join point(连接点)匹配的概念是 AOP 的关键,这使得 AOP 不同于其它仅仅提供拦截功能的旧技术。 切入点使得 advice (通知)可独立于 OO 层次。 例如,一个提供声明式事务管理的around 通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。
###9.1.2 Spring AOP capabilities and goals 功能和目标
Spring AOP 用纯 Java 实现。它不需要专门的编译过程。Spring AOP 不需要控制类装载器层次,因此它适用于 Servlet 容器或应用服务器。
Spring 目前仅支持使用方法调用作为join point(连接点)(在 Spring bean 上通知方法的执行)。 虽然可以在不影响到 Spring AOP核心 API 的情况下加入对成员变量拦截器支持,但 Spring 并没有实现成员变量拦截器。 如果你需要通知对成员变量的访问和更新连接点,可以考虑其它语言,例如 AspectJ。
Spring 实现 AOP 的方法跟其他的框架不同。Spring 并不是要尝试提供最完整的 AOP 实现(尽管 Spring AOP 有这个能力), 相反的,它其实侧重于提供一种 AOP 实现和 Spring IoC 容器的整合,用于帮助解决在企业级开发中的常见问题。
因此,Spring AOP 通常都和 Spring IoC 容器一起使用。 Aspect 使用普通的bean 定义语法(尽管 Spring 提供了强大的“autoproxying(自动代理)”功能): 与其他 AOP 实现相比这是一个显著的区别。有些事使用 Spring AOP 是无法轻松或者高效的完成的,比如说通知一个细粒度的对象。 这种时候,使用 AspectJ 是最好的选择。不过经验告诉我们: 于大多数在企业级 Java 应用中遇到的问题,只要适合 AOP 来解决的,Spring AOP 都没有问题,Spring AOP 提供了一个非常好的解决方案。
Spring AOP 从来没有打算通过提供一种全面的 AOP 解决方案来取代AspectJ。 我们相信无论是 proxy-based(基于代理 )的框架比如说Spring AOP 亦或是 full-blown 的框架比如说是 AspectJ 都是很有价值的,他们之间的关系应该是互补而不是竞争的关系。 Spring 可以无缝的整合 Spring AOP,IoC 和 AspectJ,使得所有的 AOP 应用完全融入基于 Spring 的应用体系。 这样的集成不会影响 Spring AOP API 或者AOP Alliance API;Spring AOP保留了向下兼容性。[下一章 10. Spring AOP APIs](10. Spring AOP APIs.md)会详细讨论 Spring AOP API。
一个 Spring Framework 的核心原则是 non-invasiveness(非侵袭性);这意味着你不应该在您的业务/域模型被迫引入框架特定的类和接口。然而,在一些地方,Spring Framework 可以让你选择引入 Spring Framework 特定的依赖关系到你的代码,给你这样选择的理由是因为在某些情况下它可能是更容易读或编写一些特定功能。Spring Framework (几乎)总是给你的选择:你可以自由的做出明智的决定,选择最适合您的特定用例或场景。
这样的选择与本章有关的是 AOP 框架(和 AOP 类型)选择。你可以选择AspectJ 和/或 Spring AOP ,你也可以选择 @AspectJ 注解式的方法或Spring 的 XML 配置方式。事实上,本章以介绍 @AspectJ 风格为先不应该被视为 Spring 团队倾向于 @AspectJ 的方法胜过在 Spring 的XML 配置方式。
见[9.4. Choosing which AOP declaration style to use](9.4. Choosing which AOP declaration style to use.md)里面有更完整的每种风格的使用原因探讨。
###9.1.3 AOP Proxies 代理
Spring缺省使用标准 JDK dynamic proxies(动态代理)来作为AOP的代理。这样任何接口(或者接口的 set)都可以被代理。
Spring 也支持使用 CGLIB 代理. 对于需要代理类而不是代理接口的时候CGLIB 代理是很有必要的。 如果一个业务对象并没有实现一个接口,默认就会使用 CGLIB。 此外,面向接口编程 也是一个最佳实践,业务对象通常都会实现一个或多个接口。此外,还可以强制的使用 CGLIB,在那些(希望是罕见的)在你需要通知一个未在接口中声明的方法的情况下,或者当你需要传递一个代理对象作为一种具体类型到方法的情况下。
一个重要的事实是,Spring AOP 是 proxy-based (基于代理)。见第9.6.1,“理解 AOP 代理”理解这个含义