AOP原始碼解析(三)增強器的獲取
阿新 • • 發佈:2019-02-06
普通增強器的獲取邏輯通過getAdvisor方法實現,實現步驟包括對切點的註解的獲取及根據註解資訊生成增強。
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) { validate(aif.getAspectMetadata().getAspectClass()); //切點資訊的獲取 AspectJExpressionPointcut ajexp = getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass()); if(ajexp == null) return null; else //根據切點資訊生成增強器 return new InstantiationModelAwarePointcutAdvisorImpl(this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName); }
(1)切點資訊的獲取。所謂獲取切點資訊就是指定註解的表示式資訊的獲取,如@Before("test()")
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class candidateAspectClass) { //獲取方法上的註解 AbstractAspectJAdvisorFactory.AspectJAnnotation aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if(aspectJAnnotation == null) { return null; } else { //使用AspectJExpressionPointcut例項封裝獲取的資訊 AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]); //提取得到的註解中的表示式如:@Pointcut("execution(* *.*test*(..))")中的execution(* *.*test*(..))<pre name="code" class="java">
))
ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); return ajexp; } }
//獲取指定方法上的註解並使用AspectJAnnotation封裝protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) { //設定敏感的註解類 Class classesToLookFor[] = { org/aspectj/lang/annotation/Before, org/aspectj/lang/annotation/Around, org/aspectj/lang/annotation/After, org/aspectj/lang/annotation/AfterReturning, org/aspectj/lang/annotation/AfterThrowing, org/aspectj/lang/annotation/Pointcut }; for(Class<? extends Annotation> c : classsesToLookFor) { AspectJAnnotation foundAnnotation = findAnnotation(method, c); if(foundAnnotation != null) return foundAnnotation; } return null; }
private static AspectJAnnotation findAnnotation(Method method, Class toLookFor)
{
Annotation result = AnnotationUtils.findAnnotation(method, toLookFor);
if(result != null)
return new AspectJAnnotation(result);
else
return null;
}
(2)根據切點資訊生成增強。所有的增強都有Advisor實現類InstantiationModelAwarePontcutAdvisorImpl統一瘋長。 public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName)
{
declaredPointcut = ajexp;
this.method = method;
atAspectJAdvisorFactory = af;
aspectInstanceFactory = aif;
declarationOrder = declarationOrderInAspect;
this.aspectName = aspectName;
if(aif.getAspectMetadata().isLazilyInstantiated())
{
Pointcut preInstantiationPointcut = Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), declaredPointcut);
pointcut = new PerTargetInstantiationModelPointcut(declaredPointcut, preInstantiationPointcut, aif);
lazy = true;
} else
{
instantiatedAdvice = instantiateAdvice(declaredPointcut);
pointcut = declaredPointcut;
lazy = false;
}
}
封裝過程只是簡單地將資訊封裝在類的例項中,所有的額資訊單純地複製。在例項初始化的過程中還完成了對於增強器的初始化。因為不同的增強所體現的邏輯是不同的,比如@Before(“test()”)與After(“test()”)標籤的不同就是增強器增強的位置不同,所以就需要不同的增強器來完成不同的邏輯,而根據註解中的資訊初始化對應的額增強器就是在instantiateAdvice函式中實現的。
private Advice instantiateAdvice(AspectJExpressionPointcut pcut)
{
return atAspectJAdvisorFactory.getAdvice(method, pcut, aspectInstanceFactory, declarationOrder, aspectName);
}
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)
{
Class candidateAspectClass = aif.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AbstractAspectJAdvisorFactory.AspectJAnnotation aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if(aspectJAnnotation == null)
return null;
//if we get here,we know we have an AspectJ method. Check that its an AspectJ-annotated class
if(!isAspect(candidateAspectClass))
throw new AopConfigException((new StringBuilder()).append("Advice must be declared inside an aspect type: Offending method '").append(candidateAdviceMethod).append("' in class [").append(candidateAspectClass.getName()).append("]").toString());
if(logger.isDebugEnabled())
logger.debug((new StringBuilder()).append("Found AspectJ method: ").append(candidateAdviceMethod).toString());
AbstractAspectJAdvice springAdvice;
switch(_cls4..SwitchMap.org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotationType[aspectJAnnotation.getAnnotationType().ordinal()])
{
case 1: // '\001'
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case 2: // '\002'
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case 3: // '\003'
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
if(StringUtils.hasText(afterReturningAnnotation.returning()))
springAdvice.setReturningName(afterReturningAnnotation.returning());
break;
case 4: // '\004'
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
if(StringUtils.hasText(afterThrowingAnnotation.throwing()))
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
break;
case 5: // '\005'
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
case 6: // '\006'
if(logger.isDebugEnabled())
logger.debug((new StringBuilder()).append("Processing pointcut '").append(candidateAdviceMethod.getName()).append("'").toString());
return null;
default:
throw new UnsupportedOperationException((new StringBuilder()).append("Unsupported advice type on method ").append(candidateAdviceMethod).toString());
}
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrderInAspect);
String argNames[] = parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if(argNames != null)
springAdvice.setArgumentNamesFromStringArray(argNames);
springAdvice.calculateArgumentBindings();
return springAdvice;
}
}
從函式中可以看到,Spring會根據不同的註解生成不同的增強器,例如AtBefore會對應AspectJMethodBeforeAdvice。
2.增加同步例項化增強器
如果尋找的增強器不為空而且又配置了增強延遲初始化,那麼就需要在首位加入同步例項化增強器。同步例項化增強器SyntheticInstantiationAdvisor,如下:
protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor
{
public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif)
{
super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
//目前方法前呼叫,類似@Before
public void before(Method method, Object args[], Object target)
{ //簡單初始化aspect
aif.getAspectInstance();
}
3.獲取DeclareParents註解
DeclareParents主要用於引介增強的註解形式的實現,而其實現方式馭普通增強很類似,只不過使用DeclareParentsAdvisor對功能進行封裝。
private Advisor getDeclareParentsAdvisor(Field introductionField)
{
DeclareParents declareParents = (DeclareParents)introductionField.getAnnotation(org/aspectj/lang/annotation/DeclareParents);
if(declareParents == null)
return null;
if(org/aspectj/lang/annotation/DeclareParents.equals(declareParents.defaultImpl()))
throw new IllegalStateException("defaultImpl must be set on DeclareParents");
else
return new DeclareParentsAdvisor(introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}
後面一節我們一起分析一下,Spring如何尋找匹配的增強器,篇幅較長,也比較複雜,但還是希望我們一起看下去,因為這個過程雖然很艱辛,堅持過後,你會發現,收穫是非常巨大的。