引
在上一篇結(jié)尾拴事,我們得到了增強(qiáng)的提取工作交給了List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
。接著分析惊科。
1. getAdvisors獲取增強(qiáng)簡(jiǎn)析
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 1讼撒、預(yù)處理工作,包括獲取切面類,名稱乓序,驗(yàn)證等
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
// 我們需要用一個(gè)裝飾器包裝MetadataAwareAspectInstanceFactory隔箍,這樣它只會(huì)實(shí)例化一次谓娃。
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
// 2、提取增強(qiáng)
// 2.1蜒滩、獲取切面類的所有方法滨达,循環(huán)判斷提取合適的切入點(diǎn),并創(chuàng)建增強(qiáng)
List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
// 獲取增強(qiáng)
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
// 2.2俯艰、處理perthis和pertarget切面類
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// 創(chuàng)建SyntheticInstantiationAdvisor實(shí)例
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
// 將SyntheticInstantiationAdvisor實(shí)例加入到advisors集合首位捡遍,注意:不是替換
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
// 2.3、處理引入竹握,獲取所有的引入并循環(huán)創(chuàng)建DeclareParentsAdvisor
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
在該方法中提取工作一共分為了三步:提取普通增強(qiáng)画株、處理處理perthis和pertarget、引介增強(qiáng)。篇幅有限谓传,只分析普通增強(qiáng)的處理過程,該過程也是大家最關(guān)心的過程蜈项。來看代碼:
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
// 1、驗(yàn)證
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 2续挟、提取切入點(diǎn)
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod,
aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 3紧卒、創(chuàng)建增強(qiáng)
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
在該方法中,終于看到了核心的切點(diǎn)提取和創(chuàng)建增強(qiáng)诗祸。下面分別來看這兩步是如何實(shí)現(xiàn)的跑芳。
2.提取切入點(diǎn)
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 1、從候選切入點(diǎn)上找出增強(qiáng)表達(dá)式
AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 2直颅、創(chuàng)建AspectJExpressionPointcut對(duì)象
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
2.1 從候選切入點(diǎn)上找出增強(qiáng)表達(dá)式
/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...).
*/
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
該過程主要通過findAnnotation方法完成博个,但是該方法調(diào)用較深,也不屬于我們分析的范疇际乘,感興趣的同學(xué)可以自己跟蹤調(diào)試坡倔。
2.2 創(chuàng)建AspectJExpressionPointcut對(duì)象
/**
* Create a new AspectJExpressionPointcut with the given settings.
* @param declarationScope the declaration scope for the pointcut
* @param paramNames the parameter names for the pointcut
* @param paramTypes the parameter types for the pointcut
*/
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
this.pointcutDeclarationScope = declarationScope;
if (paramNames.length != paramTypes.length) {
throw new IllegalStateException(
"Number of pointcut parameter names must match number of pointcut parameter types");
}
this.pointcutParameterNames = paramNames;
this.pointcutParameterTypes = paramTypes;
}
該創(chuàng)建過程比較簡(jiǎn)單,將提取的切點(diǎn)表達(dá)式的信息實(shí)例化為AspectJExpressionPointcut對(duì)象即可脖含。
3.創(chuàng)建增強(qiáng)
public InstantiationModelAwarePointcutAdvisorImpl(
AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod,
AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrder,
String aspectName) {
this.declaredPointcut = declaredPointcut;
this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
this.methodName = aspectJAdviceMethod.getName();
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName;
// 1罪塔、延遲初始化
// 在Spring AOP中,切面類的實(shí)例只有一個(gè)养葵,比如前面我們一直使用的MyAspect類征堪,
// 假設(shè)我們使用的切面類需要具有某種狀態(tài),以適用某些特殊情況的使用关拒,比如多線程環(huán)境佃蚜,此時(shí)單例的切面類就不符合我們的要求了。
// 在Spring AOP中着绊,切面類默認(rèn)都是單例的谐算,但其還支持另外兩種多例的切面實(shí)例的切面,即perthis和pertarget归露,
// 需要注意的是perthis和pertarget都是使用在切面類的@Aspect注解中的洲脂。
// 這里perthis和pertarget表達(dá)式中都是指定一個(gè)切面表達(dá)式,其語義與前面講解的this和target非常的相似剧包,
// perthis表示如果某個(gè)類的代理類符合其指定的切面表達(dá)式恐锦,那么就會(huì)為每個(gè)符合條件的目標(biāo)類都聲明一個(gè)切面實(shí)例;
// pertarget表示如果某個(gè)目標(biāo)類符合其指定的切面表達(dá)式疆液,那么就會(huì)為每個(gè)符合條件的類聲明一個(gè)切面實(shí)例一铅。
// 從上面的語義可以看出,perthis和pertarget的含義是非常相似的堕油。如下是perthis和pertarget的使用語法:
// perthis(pointcut-expression)
// pertarget(pointcut-expression)
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(),
this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut,
preInstantiationPointcut,
aspectInstanceFactory);
this.lazy = true;
}
// 2潘飘、立刻初始化
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
// 初始化增強(qiáng)
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
創(chuàng)建過程中又涉及到perthis和pertarget肮之,無需理會(huì),還是先看單例模式下的創(chuàng)建過程:
/**
* 根據(jù)pointcut初始化增強(qiáng)
* @param pointcut
* @return
*/
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
Advice advice = this.aspectJAdvisorFactory.getAdvice(
this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory,
this.declarationOrder,
this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 1卜录、獲取增強(qiáng)之前的處理
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
// 2局骤、針對(duì)各種不同的增強(qiáng),做不同的處理
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
// 1暴凑、前置增強(qiáng)
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 2、后置增強(qiáng)
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 3赘来、后置返回增強(qiáng)
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
// 4现喳、后置異常增強(qiáng)
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
// 5、環(huán)繞增強(qiáng)
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 6犬辰、如果是Pointcut嗦篱,則不做處理
case AtPointcut:
return null;
// 7、未能滿足case條件幌缝,拋出異常
default:
throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
}
// 3灸促、獲取增強(qiáng)方法之后,對(duì)增強(qiáng)方法進(jìn)行配置
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
看到這里涵卵,是不是有種豁然開朗的感覺浴栽,Spring針對(duì)各種不同的增強(qiáng)創(chuàng)建過程,做了不同的處理轿偎。到這里Spring創(chuàng)建增強(qiáng)的過程就完成了典鸡,對(duì)于不同的增強(qiáng)類型創(chuàng)建,大家可以自己debug跟蹤坏晦,這里不一一贅述了萝玷。
4.關(guān)于Advisor的疑問
這兩篇在介紹獲取增強(qiáng),但是最受獲取到的并不是Advice而是Advisor昆婿,可能大家會(huì)有所疑問球碉。舉例說明一下:
如果我們定義了一個(gè)DogAspect類,并用@AspectJ對(duì)其進(jìn)行注解,那么該類僅僅代表一個(gè)切面類仓蛆,會(huì)被Spring掃描并解析睁冬,僅此而已,該類不代表SpringAop概念中的切面多律。那么Spring如果通過解析該類得到具體的切面呢?
首先,關(guān)于SpringAop中的切面概念痴突,可以理解為 切面=連接點(diǎn)+增強(qiáng)
其次,而標(biāo)記了@AspectJ注解的類在被Spring解析的時(shí)候,
- 提取該類的方法上的切點(diǎn)表達(dá)式注解:例如-->@Pointcut("execution(* com.lyc.cn.v2.day07..(..))")狼荞,解析之后,就可以的到具體的切點(diǎn).
- 提取該類的方法上的增強(qiáng)注解:例如:@Before("test()")解析之后,就可以得到具體的增強(qiáng)代碼
最后,通過第一步和第二步的操作,就可以得到切點(diǎn)+增強(qiáng),那么自然就構(gòu)成了一個(gè)切面
但是Advisor接口里只包含了一個(gè)Advice,并且Advisor一般不直接提供給用戶使用,所以這里也可以理解為獲取增強(qiáng)辽装,當(dāng)然如果理解為切面也是沒有問題的。