Aspect Oriented Programming
AOP 概念
Aspect Oriented Programming(AOP)是近来较为热门的一个话题。AOP,国内
大致译作“面向方面编程”。
“面向方面编程”,这样的名字并不是非常容易理解,且容易产生一些误导。笔者不止
一次听到类似“OOP/OOD11 即将落伍,AOP 是新一代软件开发方式”这样的发言。显然,
发言者并没有理解AOP 的含义。
Aspect,没错,的确是“方面”的意思。不过,华语传统语义中的“方面”,大多数情
况下指的是一件事情的不同维度、或者说不同角度上的特性,比如我们常说:“这件事情要
从几个方面来看待”,往往意思是:需要从不同的角度来看待同一个事物。这里的“方面”,
指的是事务的外在特性在不同观察角度下的体现。
而在AOP 中,Aspect 的含义,可能更多的理解为“切面”比较合适。所以笔者更倾向
于“面向切面编程”的译法。
另外需要提及的是,AOP、OOP 在字面上虽然非常类似,但却是面向不同领域的两种
设计思想。OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,
以获得更加清晰高效的逻辑单元划分。
而AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步
骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有
着本质的差异。
上面的陈述可能过于理论化,举个简单的例子,对于“雇员”这样一个业务实体进行封
装,自然是OOP/OOD 的任务,我们可以为其建立一个“Employee”类,并将“雇员”相
关的属性和行为封装其中。而用AOP 设计思想对“雇员”进行封装将无从谈起。
同样,对于“权限检查”这一动作片断进行划分,则是AOP 的目标领域。而通过OOD/OOP
对一个动作进行封装,则有点不伦不类。
换而言之,OOD/OOP 面向名词领域,AOP 面向动词领域。
AOP 和OOD/OOP 并不冲突,我们完全可以在一个应用系统中同时应用OOD/OOP 和
AOP 设计思想,通过OOD/OOP 对系统中的业务对象进行建模,同时通过AOP 对实体处理
过程中的阶段进行隔离处理。即使不是OOD/OOP,而是在传统的POP(面向过程编程)中,
AOP 也能起到同样的作用。
将不同阶段领域加以分隔,这是否就算是AOP 呢?
AOP 还有另外一个重要特点:源码组成无关性。
11 OOD = Object Oriented Design OOP = Object Oriented Programming
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
倘若应用中通过某个具体的业务逻辑类实现了独立的权限检查,而请求调度方法通过预
编码调用这个权限模块实现权限管理。那么这也不算是AOP。对于AOP 组件而言,很重要
的一点就是源码组成无关性,所谓源码组成无关性,体现在具体设计中就是AOP 组件必须
与应用代码无关,简单来讲,就是应用代码可以脱离AOP 组件独立编译。
为了实现源码组成无关性,AOP 往往通过预编译方式(如AspectJ)和运行期动态代理
模式(如Spring AOP 和JBoss AOP)实现。
稍后章节中我们会就Spring Framework 中的AOP 实现机制进行更加深入的探讨。
下面先来看AOP 中几个比较重要的概念:
1. 切面(Aspect)
切面,对象操作过程中的截面。这可能是AOP 中最关键的一个术语。
我们首先来看一个应用开发中常见的切面:用户权限检查。大概只要是完整的应用,都
少不了用户权限检查这个模块,不同身份的用户可以做什么,不可以做什么,均由这个模块
加以判定。而这个模块调用的位置通常也比较固定:用户发起请求之后,执行业务逻辑之前。
针对权限检查这一模块进行分离,我们就得到了一个切面:
切面意义何在?
首先根据上例,假设我们实现了一个通用的权限检查模块,那么就可以在这层切面上进
行统一的集中式权限管理。而业务逻辑组件则无需关心权限方面的问题。也就是说,通过切
面,我们可以将系统中各个不同层次上的问题隔离开来,实现统一集约式处理。各切面只需
集中于自己领域内的逻辑实现。
这一方面使得开发逻辑更加清晰,专业化分工更加易于进行;另一方面,由于切面的隔
离,降低了耦合性,我们就可以在不同的应用中将各个切面组合使用,从而使得代码可重用
性大大增强。
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
2. 连接点(JoinPoint)
程序运行过程中的某个阶段点。如某个方法调用,或者某个异常被抛出。
3. 处理逻辑(Advice)
在某个连接点所采用的处理逻辑
(这里的Advice,国内不少文案中翻译为“通知”,估计是源于金山词霸,与实际含义不符,因
而这
里采用意译)
处理逻辑的调用模式通常有三种:
i. Around
在连接点前后插入预处理过程和后处理过程。
ii. Before
仅在连接点之前插入预处理过程。
iii. Throw
在连接点抛出异常时进行异常处理。
4. 切点(PointCut)
一系列连接点的集合,它指明处理方式(Advice)将在何时被触发。
上述几个概念我们将在稍后的“AOP 应用”一节中结合实际使用进行具体探讨。
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
AOP in Spring
Spring 中提供的内置AOP 支持,是基于动态AOP 机制实现。从技术角度来讲,所谓动
态AOP,即通过动态Proxy 模式,在目标对象的方法调用前后插入相应的处理代码。
而Spring AOP 中的动态Proxy 模式,则是基于Java Dynamic Proxy(面向Interface)
和CGLib(面向Class)实现。
前面曾经提及,Spring Framework 中的“事务管理”服务,实际上是借助AOP 机制
完成。我们这里就以“事务管理”为例,对动态AOP 的实现加以探讨,一方面对动态AOP
的实现原理加以探究,另一方面,也可以加深对Spring 中事务管理机制的理解。
首先,我们来看基于Java Dynamic Proxy 的AOP 实现原理。
Dynamic Proxy 与Spring AOP
Dynamic Proxy 是JDK 1.3 版本中新引入的一种动态代理机制。它是Proxy 模式的一
种动态实现版本。
我们先来看传统方式下一个Proxy 的实现实例。
假设我们有一个UserDAO 接口及其实现类UserDAOImp:
UserDAO.java:
public interface UserDAO {
public void saveUser(User user);
}
UserDAOImp.java:
public class UserDAOImp implements UserDAO{
public void saveUser(User user) {
……
}}
UserDAOImp.saveUser 方法中实现了针对User 对象的数据库持久逻辑。
如果我们希望在UserDAOImp.saveUser 方法执行前后追加一些处理过程,如启动/
提交事务,而不影响外部代码的调用逻辑,那么,增加一个Proxy 类是个不错的选择:
UserDAOProxy.java
public class UserDAOProxy implements UserDAO {
private UserDAO userDAO;
public UserDAOProxy(UserDAO userDAO) {
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
this.userDAO = userDAO;
}
public void saveUser(User user) {
UserTransaction tx = null;
try {
tx = (UserTransaction) (
new InitialContext().lookup(“java/tx”)
);
userDAO.saveUser(user);
tx.commit();
} catch (Exception ex) {
if (null!=tx){
try {
tx.rollback();
}catch(Exception e) {
}}}}}
UserDAOProxy 同样是UserDAO 接口的实现,对于调用者而言,saveUser 方法的使
用完全相同,不同的是内部实现机制已经发生了一些变化――我们在UserDAOProxy 中为
UserDAO.saveUser 方法套上了一个JTA 事务管理的外壳。
上面是静态Proxy 模式的一个典型实现。
现在假设系统中有20 个类似的接口,针对每个接口实现一个Proxy,实在是个繁琐无
味的苦力工程。
Dynamic Proxy 的出现,为这个问题提供了一个更加聪明的解决方案。
我们来看看怎样通过Dynamic Proxy 解决上面的问题:
public class TxHandler implements InvocationHandler {
private Object originalObject;
public Object bind(Object obj) {
this.originalObject = obj;
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
if (!method.getName().startsWith(“save”)) {
UserTransaction tx = null;
try {
tx = (UserTransaction) (
new InitialContext().lookup(“java/tx”)
);
result = method.invoke(originalObject, args);
tx.commit();
} catch (Exception ex) {
if (null != tx) {
try {
tx.rollback();
} catch (Exception e) {
}}}}
else {
result = method.invoke(originalObject, args);
} return result;
}}
首先注意到,上面这段代码中,并没有出现与具体应用层相关的接口或者类引用。也就
是说,这个代理类适用于所有接口的实现。
其中的关键在两个部分:
1.
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
this);
java.lang.reflect.Proxy.newProxyInstance 方法根据传入的接口类型
(obj.getClass().getInterfaces())动态构造一个代理类实例返回,这个代理类是JVM
在内存中动态构造的动态类,它实现了传入的接口列表中所包含的所有接口。
这里也可以看出,Dynamic Proxy 要求所代理的类必须是某个接口的实现
(obj.getClass().getInterfaces()不可为空),否则无法为其构造响应的动态类。这也
就是为什么Spring 对接口实现类通过Dynamic Proxy 实现AOP,而对于没有实现任何接口
的类通过CGLIB 实现AOP 机制的原因,关于CGLIB,请参见稍后章节的讨论。
2.
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
……
result = method.invoke(originalObject, args);
……
return result;
} InvocationHandler.invoke 方
法
将
在
被
代
理
类
的
方
法
被
调
用
之
前
触
发
。
通
过
这
个
方
法中,我们可以在被代理类方法调用的前后进行一些处理,如代码中所示,
InvocationHandler.invoke 方法的参数中传递了当前被调用的方法(Method),以及被
调用方法的参数。
同时,我们可以通过Method.invoke 方法调用被代理类的原始方法实现。这样,我们
就可以在被代理类的方法调用前后大做文章。
在示例代码中,我们为所有名称以“save”开头的方法追加了JTA 事务管理。
谈到这里,可以回忆一下Spring 事务配置中的内容:
<property name=”transactionAttributes”>
<props>
<prop key=”save*”>PROPAGATION_REQUIRED</prop>
<prop key=”get*”>PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
想必大家已经猜测到Spring 事务管理机制的实现原理。
是的,只需通过一个Dynamic Proxy 对所有需要事务管理的Bean 进行加载,并根据配
置,在invoke 方法中对当前调用的方法名进行判定,并为其加上合适的事务管理代码,那
么就实现了Spring 式的事务管理。
当然,Spring 中的AOP 实现更为复杂和灵活,不过基本原理一致。
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
代码胜千言,下面是笔者在客户培训过程中编写的一个Dynamic Proxy based AOP
实现示例,非常简单,有兴趣的读者可以看看。
AOPHandler.java:
public class AOPHandler implements InvocationHandler {
private static Log logger = LogFactory.getLog(AOPHandler.class);
private List interceptors = null;
private Object originalObject;
/**
* 返回动态代理实例
* @param obj
* @return
*/
public Object bind(Object obj) {
this.originalObject = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj
.getClass().getInterfaces(), this);
} /**
* 在Invoke 方法中,加载对应的Interceptor,并进行
* 预处理(before)、后处理(after)以及异常处理(exceptionThrow)过程
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
Throwable ex = null;
InvocationInfo invInfo = new InvocationInfo(proxy, method, args,
result, ex);
logger.debug(“Invoking Before Intercetpors!”);
invokeInterceptorsBefore(invInfo);
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
try {
logger.debug(“Invoking Proxy Method!”);
result = method.invoke(originalObject, args);
invInfo.setResult(result);
logger.debug(“Invoking After Method!”);
invokeInterceptorsAfter(invInfo);
} catch (Throwable tr) {
invInfo.setException(tr);
logger.debug(“Invoking exceptionThrow Method!”);
invokeInterceptorsExceptionThrow(invInfo);
throw new AOPRuntimeException(tr);
} return result;
} /**
* 加载Interceptor
* @return
*/
private synchronized List getIntercetors() {
if (null == interceptors) {
interceptors = new ArrayList();
//Todo:读取配置,加载Interceptor 实例
//interceptors.add(new MyInterceptor());
} return interceptors;
} /**
* 执行预处理方法
* @param invInfo
*/
private void invokeInterceptorsBefore(InvocationInfo invInfo) {
List interceptors = getIntercetors();
int len = interceptors.size();
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
for (int i = 0; i < len; i++) {
((Interceptor) interceptors.get(i)).before(invInfo);
}} /**
* 执行后处理方法
* @param invInfo
*/
private void invokeInterceptorsAfter(InvocationInfo invInfo) {
List interceptors = getIntercetors();
int len = interceptors.size();
for (int i = len – 1; i >= 0; i–) {
((Interceptor) interceptors.get(i)).after(invInfo);
}} /**
* 执行异常处理方法
* @param invInfo
*/
private void invokeInterceptorsExceptionThrow(InvocationInfo
invInfo) {
List interceptors = getIntercetors();
int len = interceptors.size();
for (int i = len – 1; i >= 0; i–) {
((Interceptor)
interceptors.get(i)).exceptionThrow(invInfo);
}}}Interceptor.java:
public interface Interceptor {
public void before(InvocationInfo invInfo);
public void after(InvocationInfo invInfo);
public void exceptionThrow(InvocationInfo invInfo);
} InvocationInfo.java:
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
public class InvocationInfo {
Object proxy;
Method method;
Object[] args;
Object result;
Throwable Exception;
public InvocationInfo(Object proxy, Method method, Object[] args,
Object result, Throwable exception) {
super();
this.proxy = proxy;
this.method = method;
this.args = args;
this.result = result;
Exception = exception;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Throwable getException() {
return Exception;
}
public void setException(Throwable exception) {
Exception = exception;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object getProxy() {
return proxy;
}
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
public void setProxy(Object proxy) {
this.proxy = proxy;
}}
AOPFactory.java:
public class AOPFactory {
private static Log logger = LogFactory.getLog(AOPFactory.class);
/**
* 根据类名创建类实例
* @param clzName
* @return
* @throws ClassNotFoundException
*/
public static Object getClassInstance(String clzName){
Class cls;
try {
cls = Class.forName(clzName);
return (Object)cls.newInstance();
} catch (ClassNotFoundException e) {
logger.debug(e);
throw new AOPRuntimeException(e);
} catch (InstantiationException e) {
logger.debug(e);
throw new AOPRuntimeException(e);
} catch (IllegalAccessException e) {
logger.debug(e);
throw new AOPRuntimeException(e);
}} /**
* 根据传入的类名,返回AOP 代理对象
* @param clzName
* @return
*/
public static Object getAOPProxyedObject(String clzName){
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
AOPHandler txHandler = new AOPHandler();
Object obj = getClassInstance(clzName);
return txHandler.bind(obj);
}}
MyInterceptor.java:
public class MyInterceptor implements Interceptor{
private static Log logger = LogFactory.getLog(MyInterceptor.class);
public void before(InvocationInfo invInfo) {
logger.debug(“Pre-processing”);
}
public void after(InvocationInfo invInfo) {
logger.debug(“Post-processing”);
}
public void exceptionThrow(InvocationInfo invInfo) {
logger.debug(“Exception-processing”);
}}
Spring 中Dynamic Proxy AOP 实现类为:
org.springframework.aop.framework.JdkDynamicAopProxy
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
CGLib 与Spring AOP
上面曾经提过,Dynamic Proxy 是面向接口的动态代理实现,其代理对象必须是某个
接口的实现。Dynamic Proxy 通过在运行期构建一个此接口的动态实现类完成对目标对象
的代理(相当于在运行期动态构造一个UserDAOProxy,完成对UserDAOImp 的代理任
务)。
而如果目标代理对象并未实现任何接口,那么Dynamic Proxy 就失去了创建动态代理
类的基础依据。此时我们需要借助一些其他的机制实现动态代理机制。
Spring 中,引入了CGLib 作为无接口情况下的动态代理实现。
CGLib 与Dynamic Proxy 的代理机制基本类似,只是其动态生成的代理对象并非某个
接口的实现,而是针对目标类扩展的子类。
换句话说,Dynamic Proxy 返回的动态代理类,是目标类所实现的接口的另一个实现
版本,它实现了对目标类的代理(如同UserDAOProxy 与UserDAOImp 的关系)。而CGLib
返回的动态代理类,则是目标代理类的一个子类(代理类扩展了UserDAOImp 类)。
与Dynamic Proxy 中的Proxy 和InvocationHandler 相对应,Enhancer 和
MethodInterceptor 在CGLib 中负责完成代理对象创建和方法截获处理。
下面是通过CGLib 进行动态代理的示例代码:
AOPInstrumenter.java:
public class AOPInstrumenter implements MethodInterceptor {
private static Log logger =
LogFactory.getLog(AOPInstrumenter.class);
private Enhancer enhancer = new Enhancer();
public Object getInstrumentedClass(Class clz) {
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
public Object intercept(
Object o,
Method method,
Object[] methodParameters,
MethodProxy methodProxy)
throws Throwable {
logger.debug(“Before Method =>”+method.getName());
Object result = methodProxy.invokeSuper(o, methodParameters);
logger.debug(“After Method =>”+method.getName());
return result;
}}
测试代码:
AOPInstrumenter aopInst = new AOPInstrumenter();
UserDAOImp userDAO =
(UserDAOImp) aopInst.getInstrumentedClass(UserDAOImp.class);
User user = new User();
user.setName(“Erica”);
userDAO.saveUser(user);
有兴趣的读者可以利用CGLib 对Dynamic Proxy 中给出的AOP 实现代码进行改造。
Spring 中,基于CGLib 的AOP 实现位于:
org.springframework.aop.framework.Cglib2AopProxy
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
AOP 应用
前面介绍AOP 概念的章节中,曾经以权限检查为例说明AOP 切面的概念。
权限检查的确是AOP 应用中一个热门话题,假设如果现在出现了一个设计完备的权限
管理组件,那么将是一件多么惬意的事情,我们只需要在系统中配置一个AOP 组件,即可
完成以往需要大费周张才能完成的权限判定功能。
可惜目前还没有这样一个很完善的实现。一方面权限检查过于复杂多变,不同的业务系
统中的权限判定逻辑可能多种多样(如对于某些关键系统而言,很可能出现需要同时输入两
个人的密码才能访问的需求)。另一方面,就目前的AOP 应用粒度而言,“权限管理”作为
一个切面尚显得过于庞大,需要进一步切分设计,设计复杂,实现难度较大。
目前最为实用的AOP 应用,可能就是Spring 中基于AOP 实现的事务管理机制,也正是
这一点,使得Spring AOP 大放异彩。
之前的内容中,我们大多围绕Spring AOP 的实现原理进行探讨,这里我们围绕一个简
单的AOP Interceptor 实例,看看Spring 中AOP 机制的应用与开发。
在应用系统开发过程中,我们通常需要对系统的运行性能有所把握,特别是对于关键业
务逻辑的执行效能,而对于执行效能中的执行时间,则可能是重中之重。
我们这里的实例的实现目标,就是打印出目标Bean 中方法的执行时间。
首先,围绕开篇中提到的几个重要概念,我们来看看Spring 中对应的实现。
1. 切点(PointCut)
一系列连接点的集合,它指明处理方式(Advice)将在何时被触发。
对于我们引用开发而言,“何时触发”的条件大多是面向Bean 的方法进行制定。实
际上,只要我们在开发中用到了Spring 的配置化事务管理,那么就已经进行了PointCut
设置,我们可以指定对所有save 开头的方法进行基于AOP 的事务管理:
<property name=”transactionAttributes”>
<props>
<prop key=”save*”>PROPAGATION_REQUIRED</prop>
</props>
</property>
同样,对于我们的AOP 组件而言,我们也可以以方法名作为触发判定条件。
我们可以通过以下节点,为我们的组件设定触发条件。
<bean id=”myPointcutAdvisor”
class=”org.springframework.aop.support.RegexpMethodPointcutAdv
isor”>
<property name=”advice”>
<ref local=”MyInterceptor” />
</property>
<property name=”patterns”>
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
<list>
<value>.*do.*</value>
<value>.*execute.*</value>
</list>
</property>
</bean>
RegexpMethodPointcutAdvisor 是Spring 中提供的,通过逻辑表达式指定方法
判定条件的支持类。其中的逻辑表达式解析采用了Apache ORO 组件实现,关于逻
辑表达式的语法请参见Apache ORO 文档。
上面我们针对MyInterceptor 设定了一个基于方法名的触发条件,也就是说,当
目标类的指定方法运行时,MyInterceptor 即被触发。
MyInterceptor 是我们对应的AOP 逻辑处理单元,也就是所谓的Advice。
2. Advice
Spring 中提供了以下几种Advice:
1. Interception around advice
Spring 中最基本的Advice 类型,提供了针对PointCut 的预处理、后处理过程
支持。
我们将使用Interception around advice 完成这里的实例。
2. Before advice
仅面向了PointCut 的预处理。
3. Throws advice
仅面向PointCut 的后处理过程中的异常处理。
4. After Returning advice
仅面向PointCut 返回后的后处理过程。
5. Introduction advice
Spring 中较为特殊的一种Advice,仅面向Class 层面(而不像上述Advice 面
向方法层面)。通过Introduction advice 我们可以实现多线程访问中的类锁
定。
Spring 中采用了AOP 联盟(AOP Alliance)12 的通用AOP 接口(接口定义位于
aopalliance.jar)。这里我们采用aopalliance.jar 中定义的MethodInterceptor 作为
我们的Advice 实现接口:
public class MethodTimeCostInterceptor implements
MethodInterceptor,
Serializable {
protected static final Log logger = LogFactory
12
http://aopalliance.sourceforge.net/
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
.getLog(MethodTimeCostInterceptor.class);
public Object invoke(MethodInvocation invocation) throws
Throwable {
long time = System.currentTimeMillis();
Object rval = invocation.proceed();
time = System.currentTimeMillis() – time;
logger.info(“Method Cost Time => ” + time + ” ms”);
return rval;
}}
对应配置如下:
<bean id=”MyInterceptor”
class=”net.xiaxin.interceptors.MethodTimeCostInterceptor”
/>
除此之外,我们还需要定义一个Spring AOP ProxyFactory 用以加载执行AOP 组件。
定义如下:
<bean id=”myAOPProxy”
class=”org.springframework.aop.framework.ProxyFactoryBean”>
<property name=”proxyInterfaces”>
<value>net.xiaxin.ITest</value>
</property>
<!—是否强制使用CGLIB 进行动态代理
<property name=”proxyTargetClass”>
<value>true</value>
</property>
–>
<property name=”target”>
<ref local=”test” />
</property>
<property name=”interceptorNames”>
<value>myPointcutAdvisor</value>
</property>
SpringFrameWork Developer’s Guide Version 0.6
October 8, 2004 So many open source projects. Why not Open your Documents?
</bean>
<bean id=”test” class=”net.xiaxin.Test”/>
其中的test 是我们用于测试的一个类,它实现了ITest 接口。
public interface ITest {
public abstract void doTest();
public abstract void executeTest();
}
public class Test implements ITest {
public void doTest(){
for (int i=0;i<10000;i++){}
}
public void executeTest(){
for (int i=0;i<25000;i++){}
}}
通过以上工作,我们的MyInterceptor 即被加载,并将在Test.doTest 和
Test.executeTest 方法调用时被触发,打印出这两个方法的执行时间。
public void testAOP() {
ApplicationContext ctx=new
FileSystemXmlApplicationContext(“bean.xml”);
ITest test = (ITest) ctx.getBean(“myAOPProxy”);
test.doTest();
test.executeTest();
}