AOP


Warning: Undefined array key "HTTP_REFERER" in /www/wwwroot/prod/www.enjoyasp.net/wp-content/plugins/google-highlight/google-hilite.php on line 58

?所谓AOP,即面向方面编程(Aspect Oriented Programming),方面(Aspect)”它就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。它的作用更多地是关注于系统的某一方面

?? “设计模式”的触角始终在接口与抽象中大做文章,而对于对象内部则无能为力。AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的行为封装到一个可重用模块,并将其名为“Aspect”

??? Spring AOP实现AOP技术从本质上来讲,是利用了JDK提供的动态代理技术。而从实际的实现方式来看,则是利用了IoC(Inversion of Control,反转模式)机制,同时采用了AOP联盟(AOP Alliance)的通用AOP接口。首先,Spring AOP通过xml配置文件配置了pointcut,并利用Interceptor(拦截机)作为设定的触发条件。Interceptor是由用户自定义的,它相当于是AOP中的advice,但该Interceptor需要实现AOP联盟的通用AOP接口,例如 org.aopalliance.intercept.MethodInterceptor。最后定义一个Spring AOP ProxyFactory用于加载执行AOP组件,并利用IoC机制将advice注入到接口以及实现类中。

?Spring AOP研究

Spring AOP使用纯Java实现,不需要特别的编译过程,也不需要控制类装载层次

。与JBoss AOP相同,它仍然利用了拦截器完成对方法的拦截。然而,Spring AOP

实现AOP的主要技术却主要来自于AOP联盟,如拦截器应实现

org.aopalliance.intercept.MethodInterceptor 接口,而所有advice必须实现

org.aopalliance.aop.Advice标签接口。此外,Spring实现AOP的目标也不同于其

他大部分AOP框架,它的目标不是提供及其完善的AOP实现,而是提供一个和

Spring IoC紧密整合的AOP实现,帮助解决企业应用中的常见问题。因此,Spring

AOP的功能通常是和Spring IoC容器联合使用的。AOP Advice是用普通的bean定义

语法来定义的,Advice和pointcut本身由Spring IoC 管理。这是一个重要的其他

AOP实现的区别。

3.2.3.1 切入点(pointcut)

Spring的切入点模型能够使pointcut独立于advice类型被重用。同样的pointcut

有可能接受不同的advice。将 Pointcut接口分成两个部分有利于重用类和方法的

匹配部分,并且组合细粒度的操作(如和另一个方法匹配器执行一个“并”的操

作)。

在Spring的切入点中,org.springframework.aop.Pointcut接口是重要的接口,

它用来指定通知到特定的类和方法目标。完整的接口定义如下:
public interface Pointcut
{
??? ClassFilter getClassFilter();
??? MethodMatcher getMethodMatcher();
}

ClassFilte类型也是一个接口,该接口被用来将切入点限制到一个给定的目标类

的集合。 如果matches()永远返回true,所有的目标类都将被匹配。
public interface ClassFilter
{
??? boolean matches(Class clazz);
}

MethodMatcher接口通常更加重要。完整的接口定义如下:
public interface MethodMatcher
{
??? boolean matches(Method m, Class targetClass);
??? boolean matches(Method m, Class targetClass, Object[] args);
??? boolean isRuntime();
}

matches(Method, Class) 方法被用来测试这个切入点是否匹配目标类的给定方法

。这个测试可以在AOP代理创建的时候执行,避免在所有方法调用时都需要进行测

试。如果2个参数的matches()方法对某个方法返回true,并且MethodMatcher的

isRuntime()也返回true,那么3个参数的matches()方法将在每次方法调用的时候

被调用。这使切入点能够在目标advice被执行之前立即查看传递给方法调用的参

数。由于大部分 MethodMatcher都是静态的,意味着isRuntime()方法会返回

false。此种情况下,3个参数的matches()方法永远不会被调用。

Spring AOP提供了几个实用的切入点实现,其中较为常用的是正则表达式切入点

:org.springframework.aop.support.RegexpMethodPointcut,它使用Perl 5的

正则表达式的语法。使用这个类你可以定义一个模式的列表。如果任何一个匹配

,那个切入点将被计算成 true。用法如下:
<bean id=”settersAndAbsquatulatePointcut”
???>
??? <property name=”patterns”>
??????? <list>
??????????? <value>.*get.*</value>
??????????? <value>.*absquatulate</value>
??????? </list>
??? </property>
</bean>

不过,更多情况下是直接使用RegexpMethodPointcut一个实用子类:

RegexpMethodPointcutAdvisor。它允许我们同时引用一个advice(在Spring AOP

中,advice可以是拦截器,也可以是before advice,throws advice等)。这就简

化了bean的装配,因为一个bean可以同时当作pointcut和advice,如下所示:
<bean id=”myPointcutAdvisor”

class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>
??? <property name=”advice”>
??????? <ref local=”MyInterceptor” />
??? </property>
??? <property name=”patterns”>
??????? <list>
??????????? <value>.*save.*</value>
??????????? <value>.*do.*</value>
??????? </list>
??? </property>
</bean>

注意配置文件中的myPointcutAdvisor,在Spring AOP中,一个advisor就是一个

aspect完整的模块化表示。通过advisor,可以将pointcut和advice(在此处即为

MyInterceptor)绑定起来。

3.2.3.2 通知(advice)

Spring AOP的advice可以跨越多个被advice对象共享,或者每个被advice对象有

自己的advice。要实现advice,最简单的做法就是定义一个拦截器(Interceptor

)。它采用了AOP联盟(AOP Alliance)的通用AOP接口(接口定义为

aopalliance.jar)。要实现advice,需要实现aopalliance.jar中定义的

MethodInterceptor接口。

例如,我们定义了一个业务对象接口BusinessObject及其实现类

BusinessObjectImpl,该业务对象能够存储数据,其定义如下:
public interface BusinessObject
{
??? public void save();
}
public class BusinessObjectImpl implements BusinessObject
{
??? public void save()
??? {
???????? System.out.println(“saving domain object……”);
??? }
}

现在需要为业务对象BusinessObject的Save()方法,提供Lock机制。根据Spring

AOP的实现方式,我们可以定义一个LockInterceptor来实现MethodInterceptor接

口:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LockInterceptor implements MethodInterceptor
{
??? public Object invoke(MethodInvocation invocation) throws Throwable
??? {
??????? // TODO Auto-generated method stub
??????? lock();
??????? Object ret= invocation.proceed();
??????? unlock();
??????? return ret;
??? }
??? private void lock()
??? {
??????? System.out.println(“lock domain object…”);
??? }
??? private void unlock()
??? {
??????? System.out.println(“unlock domain object…”);
??? }
}

为将interceptor与具体的advice绑定起来,需要在配置文件中配置bean:
&lt;bean id=”MyInterceptor”

class=”test.aop.spring.LockInterceptor”/&gt;

3.2.3.3 AOP代理与IoC容器

由于Spring中提供了IoC容器(例如BeanFactory),因此我们可以通过Ioc机制,

利用ProxyFactoryBean来创建 AOP代理。ProxyFactoryBean和其他Spring的

FactoryBean实现一样,引入一个间接的层次。如果你定义一个名字为foo的

ProxyFactoryBean,引用foo的对象所看到的不是 ProxyFactoryBean实例本身,

而是由实现ProxyFactoryBean的类的 getObject()方法所创建的对象。这个方法

将创建一个包装了目标对象 的AOP代理。

AOP代理利用的是Java的动态代理技术,通过它就可以加载并执行AOP组件。同时

,还需要通过IoC的方式将advice注入到接口以及其实现类。以前面的业务对象

BusinessObject为例,在xml配置文件中的配置如下:
<bean id=”myAOPProxy”

class=”org.springframework.aop.framework.ProxyFactoryBean”>
??? <property name=”proxyInterfaces”>
?????? <value>test.aop.spring.BusinessObject</value>
??? </property>
??? <property name=”target”>
?????? <ref local=”impl” />
??? </property>
??? <property name=”interceptorNames”>
?????? <value>myPointcutAdvisor</value>
??? </property>
</bean>
<bean id=”impl”/>

通过上述对pointcut、advice、advisor和AOP代理的配置,我们就可以轻易地在

Spring中实现AOP,例如:
import org.springframework.context.ApplicationContext;
import

org.springframework.context.support.FileSystemXmlApplicationContext;

public class App
{
??? private BusinessObject bo = null;
??? public static void main(String[] args)
??? {
??????? ApplicationContext ctx=new FileSystemXmlApplicationContext

(“Bean.xml”);
??????? bo= (BusinessObject) ctx.getBean(“myAOPProxy”);
??????? bo.save();
??? }
}

首先,通过AOP代理获得BusinessObject对象。当调用BusinessObject对象的

save()方法时,拦截器 LockInterceptor根据RegexpMethodPointcutAdvisor配置

的pointcut和advice之间的关系,判定该方法的调用为join point,从而拦截该

方法调用,并注入advice的执行逻辑,即lock()和unlock(),最终实现了AOP。

3.2.3.4 引入(introduction)

在Spring AOP中,将introduction当作advice来处理。与一般的advice一样,

introduction advice相当于一种特殊类型的拦截通知,需要实现

IntroductionAdvisor和IntroductionInterceptor接口,而

IntroductionInterceptor接口继承自MethodInterceptor:
public interface IntroductionInterceptor extends MethodInterceptor
{
??? boolean implementsInterface(Class intf);
}

Introduction通知不能被用于任何pointcut,因为它只能作用于类层次上,而不

是方法。我们可以只用InterceptionIntroductionAdvisor来实现导入通知,它有

下面的方法:
public interface InterceptionIntroductionAdvisor extends

InterceptionAdvisor
{
??? ClassFilter getClassFilter();
??? IntroductionInterceptor getIntroductionInterceptor();
??? Class[] getInterfaces();
}

接下来,我以JBoss AOP一节中的例子来说明introduction在Spring AOP中的应用

。我们的目标仍然是为一个已有的业务对象引入第三方接口Tracing:
public interface Tracing
{
??? void enableTracing();
??? void disableTracing();
??? boolean enabled();
}

首先,我们需要一个做大量转化的IntroductionInterceptor。在这里,我们继承

org.springframework.aop.support.DelegatingIntroductionInterceptor 实现

类。当然我们可以直接实现IntroductionInterceptor接口,但是大多数情况下

DelegatingIntroductionInterceptor是最合适的。

DelegatingIntroductionInterceptor的设计是将introduction委托到真正实现

introduction 接口的接口,隐藏完成这些工作的拦截器。委托可以使用构造方法

参数设置到任何对象中;默认的委托就是自己(当无参数的构造方法被使用时)

。这样在下面的例子里,委托是DelegatingIntroductionInterceptor的子类

TracingMixin。给定一个委托(默认是自身)的

DelegatingIntroductionInterceptor实例寻找被这个委托(而不是

IntroductionInterceptor)实现的所有接口,并支持它们中任何一个导入。子类

如TracingMixi也可能调用suppressInterflace(Class intf) 方法来隐藏不应暴

露的接口。然而,不管IntroductionInterceptor 准备支持多少接口,

IntroductionAdvisor将控制哪个接口将被实际暴露。一个导入的接口将隐藏目标

的同一个接口的所有实现。

这样,TracingMixin继承DelegatingIntroductionInterceptor并自己实现接口

Tracing。父类自动选择支持introduction的Tracing,所以我们不需要指定它。

用这种方法我们可以导入任意数量的接口。
public class TracingMixin extends DelegatingIntroductionInterceptor

implements Tracing
{
??? private boolean enabled;
??? public void enableTracing ()
??? {
??????? this.enabled = true;
??? }

??? public void disableTracing ()
??? {
??????? this. enabled = false;
??? }

??? public boolean enabled()
??? {
??????? return this.enabled;
??? }
??? public Object invoke(MethodInvocation invocation) throws Throwable
??? {?????
??????? return super.invoke(invocation);
??? }
}

通常不要需要改写invoke()方法:实现DelegatingIntroductionInterceptor就足

够了,如果是引入的方法,DelegatingIntroductionInterceptor实现会调用委托

方法, 否则继续沿着连接点处理。

所需的introduction advisor是很简单的。只需保存一个独立的TracingMixin实

例,并指定导入的接口,在这里就是Tracing。此时,TracingMixin没有相关配置

,所以我们简单地使用new来创建它。

public class TracingMixinAdvisor extends DefaultIntroductionAdvisor
{
??? public TracingMixinAdvisor() {
??????? super(new TracingMixin(),Tracing.class);
??? }
}

我们可以非常简单地使用这个advisor。它不需要任何配置。(但是,有一点是必

要的:就是不可能在没有IntroductionAdvisor 的情况下使用

IntroductionInterceptor。) 和引入一样,通常 advisor必须是针对每个实例

的,并且是有状态的。我们会有不同的TracingMixinAdvisor。每个被通知对象,

会有不同的 TracingMixin。advisor组成了被通知对象的状态的一部分。

在Spring中,Spring AOP的核心API已经基本稳定了。和Spring的其它部分一样,

AOP框架是模块化的,在保留基础设计的同时提供扩展。在Spring 1.1到1.2阶段

有很多地方可能会有所提高,但是这些地方也保留了向后兼容性。它们是:

(一)性能的提高:AOP代理的创建由工厂通过策略接口处理。因此能够支持额外

的AOP 代理类型而不影响用户代码或核心实现。
(二)更具表达力的pointcut:Spring目前提供了一个具有表达力的切入点接口

,同时添加了更多的切入点实现。Spring正在考虑提供一个简单但具有强大表达

式语言的实现。