文章目錄
1. 源碼分析aop的切面@Aspect解析
前文分析spring ioc源碼時,瞭解到,spring 解析內部註解,通常通過後置處理器進行解析,上文提到 aop注入的後置處理器AnnotationAwareAspectJAutoProxyCreator。在進行切面解析時,其實存在一些父子後置處理器之間的調用關係。
通過源碼瞭解到,@Aspect解析的起點在spring bean的實例化之前AbstractAutoProxyCreator#postProcessBeforeInstantiation方法中。
該方法主要作用是獲取spring容器中的切面類,同時將切面和通知存在緩存中,調用鏈本人通過debug形式打印出來了。
核心代碼AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors,在該方法中找到所有的通知並返回。
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
//根據父類的規則,找到所有的spring 通知
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
//獲取Bean工廠中所有AspectJ切面的通知
if (this.aspectJAdvisorsBuilder != null) {
//找到所有的通知,並放入到aspectJAdvisorsBuilder中緩存起來,並返回
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
aspectJAdvisorsBuilder這個spring內部使用的建造者模式中,會緩存所用容器中的切面,切點,通知等信息。以便後期需要代理類動態增強時進行織入。
2. 源碼分析aop使用jdk代理還是cglib動態代理?
通過源碼瞭解到,spring在實例化bean之後的後置處理器中AbstractAutoProxyCreator#postProcessAfterInitialization方法中,判斷是否需要去創建代理對象,找到動態代理創建時需要的通知,並進行代理的創建。
我將本文的重點放在了動態代理的創建,至於如何根據切點找到代理類的通知在此不進行詳細描述,這裏是創建代理類的調用鏈。
@Override
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);
}
//cgilb動態代理
return new ObjenesisCglibAopProxy(config);
}
else {
//jdk動態代理
return new JdkDynamicAopProxy(config);
}
}
在本步驟中spring aop創建了動態代理的實例,並且作爲bean的實例化對象。那麼spring aop具體是如何創建的呢?
3. spring中jdk動態代理和aop動態代理
jdk動態代理:
原理: 利用反射機制生成一個實現代理接口的實現類,在調用具體方法前調用InvokeHandler來處理。
jdk動態代理關鍵點
1.目標類(被代理類)必須實現接口。
2.自定義InvocationHandler 類實現 InvocationHandler 接口的invoke(),在invoke方法中通過反射完成代理對象的方法調用。在該過程中可以增強我們的目標對象方法調用,即織入我們自定義的邏輯。
關鍵增強的邏輯一般都在 實現該接口的invoke()方法中。
來看看spring的jdk動態代理源碼,分析粒度不大,先簡單介紹
熟悉jdk動態代理的同學就知道,jdk動態代理核心就在handler的invoke方法中,即JdkDynamicAopProxy#invoke,該類實現了InvocationHandler 接口的invoke()方法。
通過代碼可以看到動態織入通知的起點執行邏輯,
注意: 本文最後會有一張時許圖詳細描述ReflectiveMethodInvocation().proceed()方法中,通知攔截器鏈的織入過程。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
//獲取此方法的攔截鏈
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
//我們需要創建一個方法調用...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
//通過攔截器鏈進入連接點,該處邏輯與cglib相似,會執行方法攔截intercep。
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
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;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
cglib動態代理:
原理: 是利用asm,加載代理類的class文件,通過修改其字節碼生成子類,覆蓋父類的方法,來進行處理。(注意 :目標類或方法最好不要聲明成final )。
cglib動態代理關鍵點
1.目標類(被代理類)不需要實現接口。
2.通過增強器類Enhancer創建代理對象,設置織入回調的攔截器鏈setCallbackFilter,(前置通知等等都是通過此功能實現)。
3.spring aop中有一個關鍵的內部類,定義了動態通知的攔截CglibAopProxy.DynamicAdvisedInterceptor。
4.spring 的方法攔截鏈中的每個攔截器都實現了MethodInterceptor接口。
Cglib中的攔截器有點多,可以看一下CglibAopProxy的內部類帶有Interceptor的很多。
攔截器Interceptor很多,但是我們的關注點應該放在動態織入通知的攔截器中DynamicAdvisedInterceptor的invoke方法,看該方法中的方法攔截,其中和jdk動態代理織入的邏輯相似。都調用了new CglibMethodInvocation().proceed()方法。那麼通知織入的核心其實在該該類的proceed方法中。
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
//我們需要創建一個方法調用。該方法調用的proceed,就是一個通知動態織入到代理對象的過程
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
那就看一下ReflectiveMethodInvocation#proceed是如何執行的
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//我們從索引-1開始並提前增加,直至將所有同通知攔截執行完畢。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//獲取通知攔截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
//匹配動態通知攔截
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
//執行通知攔截
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
//動態匹配失敗,跳過這個攔截器,調用攔截器鏈中下一個攔截器
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
將斷點調製該處,我們可以看到通知攔截器鏈的執行順序:
afteThrowingr -> afterReturning -> after ->afterAround -> MethodBefore.
至於爲什麼是這個執行順序,肯定是有講究的,但是本人並沒有繼續深入,有興趣可以繼續追蹤一下源碼。
ok,那麼它是如何執行通知的鏈式調用的呢,代碼看了,看一張時序圖,思路會更加清晰。
4. Advice通知MethodIntercept方法攔截織入代理時序圖
個人覺得,spring的aop實現整體流程涉及到的點很多,但是大體思路並不算很複雜。真正的基礎和複雜點是jdk和cgilib如何在jvm運行時動態生成代理類。
上一篇:6、spring核心源碼解析之aop概況流程1
[下一篇:8.未完待續]