这是在网上发现的一篇关于
Spring AOP
编程的教程,读完这篇文章后,
Spring AOP
不再难以理解,因此我把它译成中文,推荐给
Spring AOP
的初学者。这是译文的
链接
。
AOP 正在成为软件开发的下一个圣杯。使用 AOP ,你可以将处理 aspect 的代码注入主程序,通常主程序的主要目的并不在于处理这些 aspect 。 AOP 可以防止代码混乱。
为了理解 AOP 如何做到这点,考虑一下记日志的工作。日志本身不太可能是你开发的主程序的主要任务。如果能将 “ 不可见的 ” 、通用的日志代码注入主程序中,那该多好啊。 AOP 可以帮助你做到。
Spring framework 是很有前途的 AOP 技术。作为一种非侵略性的,轻型的 AOP framework ,你无需使用预编译器或其他的元标签,便可以在 Java 程序中使用它。这意味着开发团队里只需一人要对付 AOP framework ,其他人还是象往常一样编程。
AOP 是很多直觉难以理解的术语的根源。幸运的是,你只要理解三个概念,就可以编写 AOP 模块。这三个概念是: advice , pointcut 和 advisor 。 advice 是你想向别的程序内部不同的地方注入的代码。 pointcut 定义了需要注入 advice 的位置,通常是某个特定的类的一个 public 方法。 advisor 是 pointcut 和 advice 的装配器,是将 advice 注入主程序中预定义位置的代码。
既然我们知道了需要使用 advisor 向主要代码中注入 “ 不可见的 ”advice ,让我们实现一个 Spring AOP 的例子。在这个例子中,我们将实现一个 before advice ,这意味着 advice 的代码在被调用的 public 方法开始前被执行。以下是这个 before advice 的实现代码:
AOP 正在成为软件开发的下一个圣杯。使用 AOP ,你可以将处理 aspect 的代码注入主程序,通常主程序的主要目的并不在于处理这些 aspect 。 AOP 可以防止代码混乱。
为了理解 AOP 如何做到这点,考虑一下记日志的工作。日志本身不太可能是你开发的主程序的主要任务。如果能将 “ 不可见的 ” 、通用的日志代码注入主程序中,那该多好啊。 AOP 可以帮助你做到。
Spring framework 是很有前途的 AOP 技术。作为一种非侵略性的,轻型的 AOP framework ,你无需使用预编译器或其他的元标签,便可以在 Java 程序中使用它。这意味着开发团队里只需一人要对付 AOP framework ,其他人还是象往常一样编程。
AOP 是很多直觉难以理解的术语的根源。幸运的是,你只要理解三个概念,就可以编写 AOP 模块。这三个概念是: advice , pointcut 和 advisor 。 advice 是你想向别的程序内部不同的地方注入的代码。 pointcut 定义了需要注入 advice 的位置,通常是某个特定的类的一个 public 方法。 advisor 是 pointcut 和 advice 的装配器,是将 advice 注入主程序中预定义位置的代码。
既然我们知道了需要使用 advisor 向主要代码中注入 “ 不可见的 ”advice ,让我们实现一个 Spring AOP 的例子。在这个例子中,我们将实现一个 before advice ,这意味着 advice 的代码在被调用的 public 方法开始前被执行。以下是这个 before advice 的实现代码:
代码:
|
package com.company.springaop.test; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class TestBeforeAdvice implements MethodBeforeAdvice { public void before(Method m, Object[] args, Object target) throws Throwable { System.out.println("Hello world! (by " + this.getClass().getName() + ")"); } } |
接口 MethodBeforeAdvice 只有一个方法 before 需要实现,它定义了 advice 的实现。 before 方法共用三个参数,它们提供了相当丰富的信息。参数 Method m 是 advice 开始后执行的方法。方法名称可以用作判断是否执行代码的条件。 Object[] args 是传给被调用的 public 方法的参数数组。当需要记日志时,参数 args 和被执行方法的名称,都是非常有用的信息。你也可以改变传给 m 的参数,但要小心使用这个功能;编写最初主程序的程序员并不知道主程序可能会和传入参数的发生冲突。 Object target 是执行方法 m 对象的引用。
在下面的 BeanImpl 类中,每个 public 方法调用前,都会执行 advice :
代码:
|
package com.company.springaop.test; public class BeanImpl implements Bean { public void theMethod() { System.out.println(this.getClass().getName() + "." + new Exception().getStackTrace()[0].getMethodName() + "()" + " says HELLO!"); } } |
类 BeanImpl 实现了下面的接口 Bean :
代码:
|
package com.company.springaop.test; public interface Bean { public void theMethod(); } |
虽然不是必须使用接口,但面向接口而不是面向实现编程是良好的编程实践, Spring 也鼓励这样做。
pointcut 和 advice 通过配置文件来实现,因此,接下来你只需编写主方法的 Java 代码:
代码:
|
package com.company.springaop.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class Main { public static void main(String[] args) { //Read the configuration file ApplicationContext ctx = new FileSystemXmlApplicationContext("springconfig.xml"); //Instantiate an object Bean x = (Bean) ctx.getBean("bean"); //Execute the public method of the bean (the test) x.theMethod(); } } |
我们从读入和处理配置文件开始,接下来马上要创建它。这个配置文件将作为粘合程序不同部分的 “ 胶水 ” 。读入和处理配置文件后,我们会得到一个创建工厂 ctx 。任何一个 Spring 管理的对象都必须通过这个工厂来创建。对象通过工厂创建后便可正常使用。
仅仅用配置文件便可把程序的每一部分组装起来。
代码:
|
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--CONFIG--> <bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.company.springaop.test.Bean</value> </property> <property name="target"> <ref local="beanTarget"/> </property> <property name="interceptorNames"> <list> <value>theAdvisor</value> </list> </property> </bean> <!--CLASS--> <bean id="beanTarget" class="com.company.springaop.test.BeanImpl"/> <!--ADVISOR--> <!--Note: An advisor assembles pointcut and advice--> <bean id="theAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theBeforeAdvice"/> </property> <property name="pattern"> <value>com/.company/.springaop/.test/.Bean/.theMethod</value> </property> </bean> <!--ADVICE--> <bean id="theBeforeAdvice" class="com.company.springaop.test.TestBeforeAdvice"/> </beans> |
四个 bean 定义的次序并不重要。我们现在有了一个 advice ,一个包含了正则表达式 pointcut 的 advisor ,一个主程序类和一个配置好的接口,通过工厂 ctx ,这个接口返回自己本身实现的一个引用。
BeanImpl 和 TestBeforeAdvice 都是直接配置。我们用一个唯一的 ID 创建一个 bean 元素,并指定了一个实现类。这就是全部的工作。
advisor 通过 Spring framework 提供的一个 RegexMethodPointcutAdvisor 类来实现。我们用 advisor 的一个属性来指定它所需的 advice-bean 。第二个属性则用正则表达式定义了 pointcut ,确保良好的性能和易读性。
最后配置的是 bean ,它可以通过一个工厂来创建。 bean 的定义看起来比实际上要复杂。 bean 是 ProxyFactoryBean 的一个实现,它是 Spring framework 的一部分。这个 bean 的行为通过一下的三个属性来定义:
- 属性proxyInterface定义了接口类。
- 属性target指向本地配置的一个bean,这个bean返回一个接口的实现。
- 属性interceptorNames是唯一允许定义一个值列表的属性。这个列表包含所有需要在beanTarget上执行的advisor。注意,advisor列表的次序是非常重要的。
Spring 工具
虽然你可以手工修改Ant构建脚本,但使用SpringUI(译注:SpringUI现在是Spring framework的一部分,并改名为spring-ide),使用Spring AOP变得很简单,只要点点鼠标即可。你可以把SpringUI安装成Eclipse的一个plug-in。然后,你只需在你的project上右击鼠标,并选择“add Spring Project Nature”。在project属性中,你可以在“Spring Project”下添加Spring配置文件。在编译前把下面的类库加入project:aopalliance.jar,commons-logging.jar,jakarta-oro-2.0.7.jar和spring.jar。运行程序时你会看到下面的信息:
... (logging information)
Hello world! (by com.company.springaop.test.TestBeforeAdvice)
com.company.springaop.test.BeanImpl.theMethod() says HELLO!
优点和缺点
Spring比起其他的framework更有优势,因为除了AOP以外,它提供了更多别的功能。作为一个轻型framework,它在J2EE不同的部分都可以发挥作用。因此,即使不想使用Spring AOP,你可能还是想使用Spring。另一个优点是,Spring并不要求开发团队所有的人员都会用它。学习Spring应该从Spring reference的第一页开始。读了本文后,你应该可以更好地理解Spring reference了。Spring唯一的缺点是缺乏更多的文档,但它的mailing list是个很好的补充,而且会不断地出现更多的文档。