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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
/*
如果返回值为该object, 则尝试返回 proxy。
以及 null 检查
*/
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
/*
最后恢复threadlocal 中之前的proxy
*/
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
拦截链
不管是 dynamic proxy 还是 cglib 都只是实现代码“织入“的手段。而真正的逻辑还是定义在拦截链上。
AdvisorChainFactory
getInterceptorsAndDynamicInterceptionAdvice
返回条件可能符合当前调用的所有Advisor。
基本逻辑是遍历所有注册的advisor,能用pointcut 过滤掉的都先过滤掉,然后用 Advice adapter 转化成MethodInterceptor
注意由于ProxyFactory 并不会在生成proxy 之前,对注册的advisor 先做一次过滤,所以实际每次调用都会都会进行这样的遍历,所以在注册advisor 的时候需要留意,不要将对当前对象无效的advisor 注册进来。
public interface AdvisorChainFactory {
List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass);
}
AdvisorChainFactory
只有 DefaultAdvisorChainFactory
一个实现。
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
/*
PointcutAdvisor 的情况下,先用其 ClassFilter 筛一下
*/
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
/*
使用 Advice Adapter 转化 PointcutAdvisor 中的 advice
*/
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
/*
用 MethodFilter 再筛一遍
*/
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
/*
static 的 advisor 的interceptor 现在已经可以直接放入拦截链里了
但如果还需要 runtime 的检查 (检查调用时传入的 arguments),
则需要将 InterceptorAndDynamicMethodMatcher 将interceptor 和 method matcher wrap 起来供runtime 检查用
这也是runtime 的 pointcut 性能较差的原因
*/
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
/*
IntroductionAdvisor 则简单取出 interceptor 即可
*/
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
/*
其他(用户自定义的) advisor
*/
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
...
}
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
封装方法调用,递归地调用拦截器。