Spring中的AOP(二)
2.5 Spring的织入
在上一篇文章中,我们介绍了Pointcut、Advice、Advisor三个必要模块,剩下的工作就是把它们拼装起来,也就是织入过程。在Spring中,使用类org.springframework.aop.framework.ProxyFactory
作为织入器。
2.5.1 认识ProxyFactory
首先,ProxyFactory
并非Spring AOP中唯一可用的织入器,而是最基本的一个织入器实现。所以,我们就从这个最基本的织入器开始,了解一下织入过程到底是什么样子的。
使用ProxyFactory
进行织入的逻辑很简单,只要为它提供生产代理对象的原材料,它就会返回那个织入完成的代理对象。我们来看示例代码:
ProxyFactory weaver = new ProxyFactory(yourTargetObject);
Advisor advisor = ...;
weaver.addAdvisor(advisor);
Object proxyObject = weaver.getProxy();
我们知道,Spring AOP在使用代理模式实现AOP的过程中采用了动态代理和CGLIB两种机制,分别对某些接口的目标类和没有实现任何接口的目标类进行代理,所以在使用ProxyFactory
对目标类进行代理的时候,会通过ProxyFactory
的某些行为控制属性对这两种情况进行区分。我们来看具体的场景,首先我们有一个接口,以及它的一个实现类:
public interface ITask {
void execute(TaskExecutionContext ctx);
}
public class MockTask implements ITask {
public void execute(TaskExecutionContext ctx) {
System.out.println("task execute.");
}
}
另外,我们还需要一个织入到Jointpoint的一个横切逻辑,也就是Advice实现。假设是PerformanceMethodInterceptor
:
// 简单的检测系统某些方法的执行性能
public class PerformanceMethodInterceptor implements MethodInterceptor {
private final Log logger = LogFactory.getLog(this.getClass());
public Object invoke(MethodInvocation invocation) throws Throwable {
StopWatch watch = new StopWatch();
try {
watch.start();
return invocation.proceed();
} catch (Exception e) {
//TODO: handle exception
} finally {
watch.stop();
if (logger.isInfoEnabled()) {
logger.info(watch.toString());
}
}
}
}
- 基于接口的代理:
MockTask
实现了ITask
接口,要对这种实现了某些接口的目标类进行代理,我们使用setInterfaces(new Class[]{ITask.class})
来明确告知ProxyFactory
我们要对ITask
接口类型进行代理。
ProxyFactory weaver = new ProxyFactory(new MockTask);
weaver.setInterfaces(new Class[]{ITask.class});
- 基于类的代理:如果目标类没有实现任何接口,
ProxyFactory
会对目标类型进行基于类的代理,即使用CGLIB。假设我们现在有一个对象,定义如下:
public class Executable {
public void execute() {
System.out.println("Executable without any Interfaces");
}
}
织入的过程如下所示:
ProxyFactory weaver = new ProxyFactory(new Executable());
weaver.addAdvisor(advisor);
Executable proxyObject = (Executable)weaver.getProxy();
proxyObject.execute();
但是,即使目标对象类实现了至少一个接口,我们也可以通过proxyTargetClass
属性强制ProxyFactory
采用基于类的代理。除此之外,如果将ProxyFactory
的optimize
属性设为true的话,也会采用基于类的代理机制。
2.5.2 看清ProxyFactory的本质
我们不仅要知晓怎么使用ProxyFactory
,并且还需要了解它其中的内部实现,这可以帮我们更好地使用它。我们先从它的根说起:
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader)
}
这个接口对Spring AOP框架内的不同代理实现机制做了适度的抽象,目前,Spring提供了基于JDK动态代理和CGLIB两种机制的AopProxy
实现。
首先,我们来看工厂类AopProxyFactory
的定义:
public interface AopProxyFactory {
AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}
AopProxyFactory
的实现类就一个,DefaultAopProxyFactory
,它会根据传入的AdvisedSupport
示例提供的相关信息,来决定生成什么类型的AopProxy
,实现逻辑很简单,伪代码如下:
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
// 创建Cglib2AopProxy实例,并返回
} else {
// 创建JdkDynamicAopProxy实例,并返回
}
可以发现,生成的AopProxy
类型是由AdvisedSupport
实例来决定的,我们来看一下这个类到底是何方神圣。
AdvisedSupport
继承了ProxyConfig
类并实现了Advised
接口。我们先来看ProxyConfig
类,它其实就是个普通的JavaBean,它定义了5个boolean类型的属性,分别为proxyTargetClass
(指定是否使用CGLIB代理)、optimize
(告知代理对象是否要进行进一步优化,若为true,则使用CGLIB,默认为false)、opaque
(控制代理对象是否可以强制转为Advised
类型)、exposeProxy
、frozen
。
要生成代理对象,光有ProxyConfig
提供的控制信息还不够,我们还需要生成代理对象的一些具体信息,比如Jointpoint和Advice,这些信息可以通过实现Advised
接口来设置或者查询(addAdvisor()/removeAdvisor()
)。
那么,AopProxy
、AdvisedSupport
与ProxyFactory
是什么关系呢?
ProxyFactory
集AopProxy
和AdvisedSupport
于一身,所以可以通过它设置生成对象所需要的相关信息,也可以通过它取得最终生成的代理对象。前者是AdvisedSupport
的职责,后者是AopProxy
的职责。
至此,我们已经了解了ProxyFactory
这个最基本的织入器实现,它还有几个兄弟,比如AspectJProxyFactory
和ProxyFactoryBean
,它们的关系如下所示,在此不作进一步说明,有兴趣的读者可以查阅《Spring揭秘》P179。
参考资料:《Spring揭秘》王福强