Spring AOP 的实现
Spring AOP 不同于 AspectJ 的 AOP 实现,是在 runtime,通过代理原本的 object 来实现的。
由于 Spring IOC 的 container 的存在,编程过程中是通过container 间接地访问对象,Spring 可以将原本的object 替换为 proxy ,而不侵入原本的代码。
Proxy 有两种产生方式,通过 Java dynamic proxy 和 CGLIB 。本章只涉及 dyanmic proxy 的实现方法。 (dynamic proxy 的基础不在本章范围内)
ProxyFactory
ProxyFactory
提供了手动创建代理的方法。是比较好的一个分析 Proxy 生成过程的切入点。通过设置需要被代理的object 以及对应的advisors 就可以生成相应的代理。它和被代理的object 是一一对应的关系。
// 创建代理示例
ProxyFactory factory = new ProxyFactory();
// 设置target object
factory.setTarget(myBusinessInterfaceImpl);
// 设置interface 及 advice/advisor
factory.setInterface(MyBusinessInterface);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
其结构也很简单,通过继承 ProxyCreatorSupport
,实现了通用的一些代理的配置以及对Advisor, Advice, interfaces 等的管理
Proxy 配置和管理
配置主要由 ProxyConfig
完成,Advise/Advisor的注册管理则定义在Advised
里。
ProxyConfig
定义了下列参数。
private boolean proxyTargetClass = false;
private boolean optimize = false;
boolean opaque = false;
boolean exposeProxy = false;
private boolean frozen = false;
proxyTargateClass
. 是否使用CGLIBoptimize
. 是否采用CGLIB 的优化opaque
. 是否允许生成的proxy 被cast 成 Advised ——— 通过Advised 可以知道该proxy 的切面等信息exposeProxy
. 是否要把当前proxy 暴露在threadlocal 中。从而可以通过AopContext.currentProxy()
获取该proxy。一个使用场景是,如果希望在被proxy的class中调用proxy的方法,则需要开启此功能,从threadlocal 中获取proxy。frozen
. 是否允许继续修改配置,可以通过将其设置为 true 来避免被意外修改。
Advised
Advised
和被代理的object 是一一对应的关系,定义了一些列管理加于被代理object 上的proxy 的方法 —— 管理interfaces, advices, advisors 等
- ProxyConfig 中的参数的获取
- Proxied Interface 的管理
- Targate Source 的管理
- Advice List 的管理
- Advisor List 的管理
TargateSource
我们并不直接访问被代理的对象,而是通过TargateSource
来访问。TargateSource
封装了被proxy 的object, 提供了对proxied object 的间接访问。这让我们可以不透明地替换下面实际的proxied object。
public interface TargetSource extends TargetClassAware {
Class> getTargetClass();
boolean isStatic();
Object getTarget() throws Exception;
void releaseTarget(Object target) throws Exception;
}
通常用SingletonTargetSource
访问固定 proxied object 就可以了。
AdvisedSupport
上述两个配置由 AdvisedSupport
合起来提供一个convenience implementation
public class AdvisedSupport extends ProxyConfig implements Advised {}
实现细节上,AdvisedSupport
默认使用SingletonTargetSource
advice 添加的时候会被转化为 DefaultPointcutAdvisor,对所有的class/method 都生效。
另外会有对Introduction advice 的一些处理。例如会将 introduction interface 加到 proxied interfce 里。
生成 Proxy
ProxyFactory
将配置和管理的工作都交给了 AdvisedSupport
,而将具体的生成的代理的工作 delegate 给了 AopProxy
。
生成 proxy 的入口如下
public Object getProxy() {
return createAopProxy().getProxy();
}
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
AopProxy
两种实现,有基于Java dynamic proxy 的实现 ——JdkDynamicAopProxy
和基于CGLIB 的实现 —— CglibAopProxy
DefaultAopProxyFactory
具体AopProxy
实现会经由工厂类 DefaultAopProxyFactory
决定,通过判断 config 的 optimize, proxyTargetClass, 以及proxied interfaces 来判断用那个一 AopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
JdkDynamicAopProxy
JdkDynamicAopProxy
是实际生成proxy 的地方
JdkDynamicAopProxy
实现 InvocationHandler
, 用Java dynamic proxy生成代理
Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
Proxied Interfaces
而proxiedInterfaces 由AopProxyUtils.completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) 生成,除了我们添加到ProxyFactory 的interface 之外,还会添加三个特殊的 interface, SpringProxy
, Advised
, DecoratingProxy
。
SpringProxy
- 空interface, 用来标记该object 是spring 生成的 proxy
Advised
- 令proxy 也能访问到创建它的proxy 信息,如果 ProxyConfig.opaque = true 则不添加这个 interface。
DecoratingProxy
- 令AOP 外部的 package 也能检查 proxy 上的 interface
Advised
和 DecoratingProxy
上的方法的调用会被拦截下来,具体实现视AopProxy
的实现而定。
invoke()
invoke()
是 JdkDynamicAopProxy
的核心方法,调用的拦截,转发都是在这里完成。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
try {
...
/*
拦截对 DecoratingProxy 和 Advised 方法的调用
*/
else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
/*
在threadlocal 中暴露当前proxy
*/
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
/*
从TargetSource 中获取object
*/
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
/*
创建interceptor 链并调用
*/
List
拦截链
不管是 dynamic proxy 还是 cglib 都只是实现代码“织入“的手段。而真正的逻辑还是定义在拦截链上。
AdvisorChainFactory
getInterceptorsAndDynamicInterceptionAdvice
返回条件可能符合当前调用的所有Advisor。
基本逻辑是遍历所有注册的advisor,能用pointcut 过滤掉的都先过滤掉,然后用 Advice adapter 转化成MethodInterceptor
注意由于ProxyFactory 并不会在生成proxy 之前,对注册的advisor 先做一次过滤,所以实际每次调用都会都会进行这样的遍历,所以在注册advisor 的时候需要留意,不要将对当前对象无效的advisor 注册进来。
public interface AdvisorChainFactory {
List
AdvisorChainFactory
只有 DefaultAdvisorChainFactory
一个实现。
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
@Override
public List
ReflectiveMethodInvocation
ReflectiveMethodInvocation
将一次调用和上面的拦截链组合起来
public Object proceed() throws Throwable {
/*
拦截链遍历完,调用原本的方法
*/
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
/*
获取并调用拦截器的 invoke() 方法,传入自身
而拦截器中,执行了自身逻辑之后由会调用 MethodInvocation.proceed() 方法,
从而递归地实现对拦截链的遍历
*/
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
/*
做runtime 的 MethodMatcher 检查
*/
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
至此整个拦截调用,用了一种很巧妙的方法,将advice代码的执行时间交给了拦截器自己决定。这种方法灵活度更高也更容易实现。而不是通过维护拦截链顺序,比如将before advice 放在原方法调用前,after returing 放在调用链后。
总结
Spring AOP runtime 的代码织入有两种方式,Java dynamic proxy 和 cglib。代码织入方式和 AOP 的实现调用无关。
AOP 后的方法调用每次都会遍历过滤所有的advisor,所以不要注册无效的advisor。
调用链中的 Interceptor 通过改变 MethodInvocation.invoke() 的位置来改变切入时机。
ReflectiveMethodInvocation
封装方法调用,递归地调用拦截器。