这篇文章是我对一篇文章修改进行发布的,修改了许多我认为不容易理解的地方,同时也将格式改美观,以方便大家阅读。
我们只需要理解三个概念就可以编写AOP模块了。这三个概念是:advice,pointcut和advisor。
advice :advice是你想向别别的程序内部不同的地方注入的代码。
pointcut: 定义需要注入advice的位置。
advisor:是advice和pointcut的装配器,是将advice注入主程序预定位置的代码。
既然我们知道了需要使用advisor向主要代码中注入“不可见的”advice,那么让我们实现一个Spring AOP的例子。在这个例子中,我们将实现一个before advice,即这个advice的代码在「目标方法」被调用前开始执行。以下是这个before advice的实现代码:
package com.spring;
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("before advice 需要其它程序中注入的代码,在被注入程序执行之前执行" );
}
}
在下面的BeanImpl类中,theMethod()是
「
目标方法
」
。它将在before advice执行完之后开始执行。
package com.spring;
public class BeanImpl implements Bean{
public void theMethod() {
/**被其他代码注入的代码*/
System.out.println("theMethod() 注入代码执行之后执行");
}
}
类BeanImpl实现了下面的接口Bean:
package com.spring;
public interface Bean {
/**接口*/
package com.spring;
}
虽然不是必须使用接口,但面向接口而不是面向实现编程是良好的编程实践,Spring也鼓励这样做。
pointcut和advice的「装配工作」是通过配置文件来实现,因此,接下来你只需编写主方法的Java代码:
package com.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//读取配置文件
ApplicationContext ctx
= new FileSystemXmlApplicationContext("springconfig.xml" );
//实例化对象
Bean x = (Bean) ctx.getBean( "bean" );
// 执行bean里的theMethod方法
x.theMethod();
}
}
<? 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.spring.Bean</ value >
</property >
<!-- 目标 -->
<property name= "target">
<ref local= "beanTarget"/>
</property >
<!-- 拦截器(advisor:装载器) -->
<property name= "interceptorNames">
<list >
< value> theAdvisor </value >
</list >
</property >
</bean >
<!--CLASS 目标代码-->
<bean id= "beanTarget" class ="com.spring.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\.spring\.Bean\.theMethod </value >
</property >
</bean >
<!--ADVICE 被注入代码-->
<bean id= "theBeforeAdvice" class ="com.spring.TestBeforeAdvice" />
</ beans>
四个bean定义的次序并不重要。
我们现在有了一个advice,一个advisor,一个主程序类和一个配置好的接口。通过工厂ctx,这个接口返回自己本身实现的一个引用。
BeanImpl和TestBeforeAdvice都是直接配置。我们用一个唯一的ID(如: id = "beanTarget" 和 id = " theBeforeAdvice " ) 创建一个bean元素,并指定了一个实现类。这就是全部的工作。
advisor通过Spring framework提供的一个RegexMethodPointcutAdvisor类来实现。我们用advisor的一个属性来指定它所需的advice-bean。第二个属性则用正则表达式定义了pointcut,确保良好的性能和易读性。
最后配置的是bean,它可以通过一个工厂来创建。bean的定义看起来比实际上要复杂。bean是ProxyFactoryBean的一个实现,它是Spring framework的一部分。这个bean的行为通过一下的三个属性来定义:
- 属性proxyInterface定义了接口类。
- 属性target指向本地配置的一个bean,这个bean返回一个接口的实现。
- 属性interceptorNames是唯一允许定义一个值列表的属性。这个列表包含所有需要在beanTarget上执行的advisor。注意,advisor列表的次序是非常重要的。