AOP使这些服务模块化,然后在应用它们的时候不需要请求。这使那些对象跟紧密,而且专注于自己需要的地方,完全忽视它们需要执行的系统服务。简短地说,AOP使POJO类保持简单。
可以理解为aspect是一个毯子,覆盖在系统的一些组件上,如下图所示。它中心是一些系统组件,用来执行业务逻辑。通过AOP,你可以把你的核心模块用一些功能层来覆盖。这些功能层不需要你的组件知道就可以灵活的执行,也不用声明。这是个强力概念,哈哈,它把安全等东西从杂乱的业务中拿出来。
1.4.2 实战AOP
假设你的程序已经给了市场部,他们返回了一些新需求,一个诗人必须跟一个骑士一起走,编写骑士的行为,写成一首诗。
Hmm…一个唱着骑士之歌的诗人,eh??听起来不难。下面写下了诗人class。
package com.springinaction.chapter01.knight;
import org.apache.log4j.Logger;
public class Minstrel {
private static final Logger SONG = Logger.getLogger(Minstrel.class);
public void singBefore (Knight knight){
SONG.info("Fa la la; Sir " + knight.getName() + " is so brave!");
}
public void singAfter(Knight knight){
SONG.info("Tee-hee-he; Sir " + knight.getName() + " did embark a quest!");
}
}
用DI的思想,KnightOfRoundTable应该做些修改,添加minstrel进去。
private Minstrel minstrel;
……
public void setMinstrel(Minstrel minstrel) {
this.minstrel = minstrel;
}
……
public Object embarkOnQuest() throws GrailFailedException{
minstrel.singBefore(this);
HolyGrail grail = quest.embark();
minstrel.singAfter(this);
return grail;
}
应该就是这样的!等等…还有个小问题。每个knight要停下来告诉minstrel他要进行quest了,然后才能继续。如下图所示。Quest后,knight必须告诉minstrel唱他的歌。记得告诉minstrel唱歌会阻止knight进行quest-embark。
理想情况,minstrel要自动的开始唱歌。Knight不需要知道自己的行为写入了诗中。毕竟,你不想knight为了告诉minstrel而耽误quest。
简短的说,minstrel的行为超越了knight的行为。另外一个说法是:minstrel的服务(唱歌)同knight的行为正交了。这就需要把minstrel变成一个aspect,然后用来写诗。然后,minstrel的服务就覆盖了knight的行为,所有的都不需要knight知道有个minstrel在那里。下图所示:
就像看到的,通过Spring的AOP很容易把minstrel变成一个aspect。
组织aspect
Spring中有几个方法来实现aspect,我们在第四章专研这些。但为了下面的例子,我们介绍Spring2.0中提供的AOP命名空间。为了使用aop,那么要通过xml来声明命名空间。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.1.xsd">
……………
</beans>
声明了命名空间,我们就可以创建aspect。下面xml中,在Spring上下文中的minstrel作为一个bean,接着就可以同knight写作的aspect。
<bean id="minstrel" class="com.springinaction.chapter01.knight.Minstrel">
<aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut id="questPointcut" expression="execution(* *.embarkQuest(..)) and target(bean)"/>
<aop:before method="singBefore" pointcut-ref="questPointcut" arg-names="bean"/>
<aop:after-returning method="singAfter" pointcut-ref="questPointcut" arg-names="bean"/>
</aop:aspect>
</aop:config>
</bean>
上面的代码有很多要了解的。
l 第一行,声明minstrel bean,它没有任何的依赖关系。
l aop:config,大部分的aop配置都要在这个元素内。
l aop:aspect 元素声明了一个aspect,ref指向了需要生成的类的id
l aop:pointcut一个aspect由pointcut组成。Pointcut是指哪里使用aspect的功能。这个元素定义了执行embarkQuest时触发的一个pointcut。
l aop:before 定义了在pointcut之前要调用的方法
l aop:after 定义了在pointcut之后调用的方法
这就是所有的。我们现在把minstrel变成了一个Spring的aspect。不要担心不理解这些,第四章有足够的例子让你明白Spring AOP的。现在,有两个重要点。
第一,Minstrel仍然是个POJO类,没有什么表面它是个aspect,但在Spring上下文中,它已经是个aspect了。
第二,更重要的,knight不需要告诉minstrel歌唱他的事迹了。作为一个aspect,minstrel自动处理这些。实际上,knight再也不用知道minstrel存在了,相应的,KnightOfRoundTable中之前添加的代码将删掉。
public class KnightOfRoundTable implements Knight{
private String name;
private Quest quest;
public KnightOfRoundTable(String name) {
this.name = name;
}
public Object embarkOnQuest() throws GrailFailedException{
return quest.embark();
}
public void setQuest(Quest quest){
this.quest = quest;
}
}
使用AOP来记录knight的事迹很有趣,但Spring AOP可以做更多有用的事。以后将看到,Spring 使用AOP来提供企业级服务,比如声明事务(第六章)和安全(第七章)。