Spring源碼分析之AOP從解析到調(diào)用

首先丈氓,為了讓大家能更有效的理解AOP,先帶大家過一下AOP中的術(shù)語:

切面(Aspect):指關(guān)注點模塊化熊尉,這個關(guān)注點可能會橫切多個對象裸弦。事務(wù)管理是企業(yè)級Java應(yīng)用中有關(guān)橫切關(guān)注點的例子。在Spring AOP中楷掉,切面可以使用在普通類中以@Aspect注解來實現(xiàn)厢蒜。

連接點(Join point):在Spring AOP中,一個連接點總是代表一個方法的執(zhí)行烹植,其實就代表增強的方法郭怪。

通知(Advice):在切面的某個特定的連接點上執(zhí)行的動作。通知有多種類型刊橘,包括around, before和after等等颂鸿。許多AOP框架促绵,包括Spring在內(nèi),都是以攔截器做通知模型的嘴纺,并維護(hù)著一個以連接點為中心的攔截器鏈败晴。

目標(biāo)對象(Target):目標(biāo)對象指將要被增強的對象。即包含主業(yè)務(wù)邏輯的類的對象栽渴。

切點(Pointcut):匹配連接點的斷言尖坤。通知和切點表達(dá)式相關(guān)聯(lián),并在滿足這個切點的連接點上運行(例如闲擦,當(dāng)執(zhí)行某個特定名稱的方法時)慢味。切點表達(dá)式如何和連接點匹配是AOP的核心:Spring默認(rèn)使用AspectJ切點語義。

顧問(Advisor): 顧問是Advice的一種包裝體現(xiàn)墅冷,Advisor是Pointcut以及Advice的一個結(jié)合纯路,用來管理Advice和Pointcut。

織入(Weaving):將通知切入連接點的過程叫織入

引入(Introductions):可以將其他接口和實現(xiàn)動態(tài)引入到targetClass中

一個栗子

術(shù)語看完了寞忿,我們先上個Demo回顧一下吧~

首先驰唬,使用EnableAspectJAutoProxy注解開啟我們的AOP

@ComponentScan(basePackages = {"com.my.spring.test.aop"})

@Configuration

@EnableAspectJAutoProxy

public class Main {

public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

IService service = context.getBean("service", IService.class);

service.doService();

}

}

寫一個接口

public interface IService {

void doService();

}

寫一個實現(xiàn)類

@Service("service")

public class ServiceImpl implements IService{

@Override

public void doService() {

System.out.println("do service ...");

}

}

寫一個切面

@Aspect

@Component

public class ServiceAspect {

@Pointcut(value = "execution(* com.my.spring.test.aop.*.*(..))")

public void pointCut() {

}

@Before(value = "pointCut()")

public void methodBefore(JoinPoint joinPoint) {

String methodName = joinPoint.getSignature().getName();

System.out.println("執(zhí)行目標(biāo)方法 【" + methodName + "】 的【前置通知】,入?yún)ⅲ? + Arrays.toString(joinPoint.getArgs()));

}

@After(value = "pointCut()")

public void methodAfter(JoinPoint joinPoint) {

String methodName = joinPoint.getSignature().getName();

System.out.println("執(zhí)行目標(biāo)方法 【" + methodName + "】 的【后置通知】,入?yún)ⅲ? + Arrays.toString(joinPoint.getArgs()));

}

@AfterReturning(value = "pointCut()")

public void methodReturn(JoinPoint joinPoint) {

String methodName = joinPoint.getSignature().getName();

System.out.println("執(zhí)行目標(biāo)方法 【" + methodName + "】 的【返回通知】叫编,入?yún)ⅲ? + Arrays.toString(joinPoint.getArgs()));

}

@AfterThrowing(value = "pointCut()")

public void methodThrow(JoinPoint joinPoint) {

String methodName = joinPoint.getSignature().getName();

System.out.println("執(zhí)行目標(biāo)方法 【" + methodName + "】 的【異常通知】辖佣,入?yún)ⅲ? + Arrays.toString(joinPoint.getArgs()));

}

}

測試運行

執(zhí)行目標(biāo)方法 【doService】 的【前置通知】,入?yún)ⅲ篬]

do service ...

執(zhí)行目標(biāo)方法 【doService】 的【返回通知】搓逾,入?yún)ⅲ篬]

執(zhí)行目標(biāo)方法 【doService】 的【后置通知】卷谈,入?yún)ⅲ篬]

以上

Demo看完了,運行效果也出來了恃逻,AOP已生效雏搂,但如何生效的呢?相比于我們普通使用Bean的Demo寇损,在這里凸郑,我們只不過加上了一個@EnableAspectJAutoProxy注解以及一個標(biāo)識了@Aspectj的類,那么我們先看看@EnableAspectJAutoProxy這個注解做了什么吧~

開啟AOP

以下是筆者所畫的大致流程圖

? 其中AspectJAutoProxyRegistrar實現(xiàn)了ImportBeanDefinitionRegistrar矛市,所以在處理BeanFactoryPostProcessor邏輯時將會調(diào)用registerBeanDefinitions方法芙沥,此時就會把AnnotationAwareAspectJAutoProxyCreator注冊到容器中,其中BeanFactoryPostProcessor的邏輯就不再說了浊吏,往期文章有過詳細(xì)分析而昨。而AnnotationAwareAspectJAutoProxyCreator的類圖如下:

我們發(fā)現(xiàn)AnnotationAwareAspectJAutoProxyCreator是實現(xiàn)了BeanPostProcessor接口的類,所以它其實是一個后置處理器找田,那么歌憨,還記得在創(chuàng)建Bean過程中的BeanPostProcessor九次調(diào)用時機嗎?不記得也沒關(guān)系墩衙,AnnotationAwareAspectJAutoProxyCreator起作用的地方是在bean的實例化前以及初始化后务嫡,分別對應(yīng)著解析切面和創(chuàng)建動態(tài)代理的過程,現(xiàn)在漆改,就讓我們先來看看解析切面的過程吧~

解析切面

解析切面的流程如下圖所示:

我們已經(jīng)了解到切面解析的過程是由AnnotationAwareAspectJAutoProxyCreator完成的心铃,而AnnotationAwareAspectJAutoProxyCreator又繼承了AbstractAutoProxyCreator,所以首先挫剑,我們先會來到AbstractAutoProxyCreator#postProcessBeforeInstantiation

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {

// class類型是否為(Advice, Pointcut, Advisor, AopInfrastructureBean)

? // shouldSkip中將會解析切面

? if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {

? ? this.advisedBeans.put(cacheKey, Boolean.FALSE);

? ? return null;

? }

}

調(diào)用到子類的AspectJAwareAdvisorAutoProxyCreator#shouldSkip

@Override

protected boolean shouldSkip(Class<?> beanClass, String beanName) {

? // 尋找advisor

? List<Advisor> candidateAdvisors = findCandidateAdvisors();

? for (Advisor advisor : candidateAdvisors) {

? ? if (advisor instanceof AspectJPointcutAdvisor &&

? ? ? ? ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {

? ? ? return true;

? ? }

? }

? return super.shouldSkip(beanClass, beanName);

}

findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors() {

? // 尋找實現(xiàn)了Advisor接口的類, 由于我們一般不會以接口的方式實現(xiàn)切面去扣,這里返回null

? List<Advisor> advisors = super.findCandidateAdvisors();

? if (this.aspectJAdvisorsBuilder != null) {

? ? // 這里將解析出所有的切面

? ? advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());

? }

? return advisors;

}

buildAspectJAdvisors

public List<Advisor> buildAspectJAdvisors() {

? // aspectBeanNames有值則說明切面已解析完畢

? List<String> aspectNames = this.aspectBeanNames;

? // Double Check

? if (aspectNames == null) {

? ? synchronized (this) {

? ? ? aspectNames = this.aspectBeanNames;

? ? ? if (aspectNames == null) {

? ? ? ? List<Advisor> advisors = new ArrayList<>();

? ? ? ? aspectNames = new ArrayList<>();

? ? ? ? // 取出是Object子類的bean,其實就是所有的bean

? ? ? ? String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(

? ? ? ? ? this.beanFactory, Object.class, true, false);

? ? ? ? for (String beanName : beanNames) {

? ? ? ? ? // 獲得該bean的class

? ? ? ? ? Class<?> beanType = this.beanFactory.getType(beanName);

? ? ? ? ? // 判斷是否有標(biāo)識@AspectJ注解

? ? ? ? ? if (this.advisorFactory.isAspect(beanType)) {

? ? ? ? ? ? // 將beanName放入集合中

? ? ? ? ? ? aspectNames.add(beanName);

? ? ? ? ? ? // 將beanType和beanName封裝到AspectMetadata中

? ? ? ? ? ? AspectMetadata amd = new AspectMetadata(beanType, beanName);

? ? ? ? ? ? // Kind默認(rèn)為SINGLETON

? ? ? ? ? ? if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {

? ? ? ? ? ? ? MetadataAwareAspectInstanceFactory factory =

? ? ? ? ? ? ? ? new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

? ? ? ? ? ? ? // 這里會通過@Before @After等標(biāo)識的方法獲取到所有的advisor

? ? ? ? ? ? ? List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

? ? ? ? ? ? ? if (this.beanFactory.isSingleton(beanName)) {

? ? ? ? ? ? ? ? // 將獲取到的所有advisor放入緩存

? ? ? ? ? ? ? ? this.advisorsCache.put(beanName, classAdvisors);

? ? ? ? ? ? ? }

? ? ? ? ? ? ? advisors.addAll(classAdvisors);

? ? ? ? ? ? }

? ? ? ? ? }

? ? ? ? }

? ? ? ? // 將所有解析過的beanName賦值

? ? ? ? this.aspectBeanNames = aspectNames;

? ? ? ? return advisors;

? ? ? }

? ? }

? }

? // aspectNames不為空樊破,意味有advisor愉棱,取出之前解析好的所有advisor

? List<Advisor> advisors = new ArrayList<>();

? // 獲取到所有解析好的advisor

? for (String aspectName : aspectNames) {

? ? List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);

? ? if (cachedAdvisors != null) {

? ? ? advisors.addAll(cachedAdvisors);

? ? }

return advisors;

}

advisorFactory.getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {

// 獲取到標(biāo)識了@AspectJ的class,其實就是剛剛封裝的class

? Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();

? // 獲取className

? String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();


? List<Advisor> advisors = new ArrayList<>();


? // 拿出該類除了標(biāo)識@PointCut的所有方法進(jìn)行遍歷 getAdvisorMethods時會對method進(jìn)行一次排序

? // 排序順序 Around, Before, After, AfterReturning, AfterThrowing

? for (Method method : getAdvisorMethods(aspectClass)) {

? ? // 獲取到advisor

? ? Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);

? ? if (advisor != null) {

? ? ? // 加入到集合中

? ? ? advisors.add(advisor);

? ? }

? }

}

我們先看下getAdvisorMethods方法

private List<Method> getAdvisorMethods(Class<?> aspectClass) {

? final List<Method> methods = new ArrayList<>();

? // 循環(huán)遍歷該類和父類的所有方法

? ReflectionUtils.doWithMethods(aspectClass, method -> {

? ? // 排除@PointCut標(biāo)識的方法

? ? if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {

? ? ? methods.add(method);

? ? }

? }, ReflectionUtils.USER_DECLARED_METHODS);

? if (methods.size() > 1) {

? ? // 以Around, Before, After, AfterReturning, AfterThrowing的順序自定義排序

? ? methods.sort(METHOD_COMPARATOR);

? }

? return methods;

}

不知道小伙伴們對ReflectionUtils.doWithMethods這個工具類熟不熟悉呢哲戚,這個工具類在之前分析Bean創(chuàng)建過程時可是出現(xiàn)了好多次呢羽氮,并且我們也是可以使用的

現(xiàn)在,已經(jīng)獲取到切面中的所有方法了惫恼,那么接下來就該對這些方法解析并進(jìn)行封裝成advisor了~

getAdvisor

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,

int declarationOrderInAspect, String aspectName) {

// 獲取方法上的切點表達(dá)式

? AspectJExpressionPointcut expressionPointcut = getPointcut(

? ? candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());

? // 封裝成對象返回档押,創(chuàng)建對象時將會解析方法創(chuàng)建advice

? return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? this, aspectInstanceFactory, declarationOrderInAspect, aspectName);

}

獲取切點表達(dá)式的過程其實非常簡單,即是解析方法上的注解,取出注解上的value即可

getPointcut

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {

? // 查找方法上和AspectJ相關(guān)注解

? AspectJAnnotation<?> aspectJAnnotation =

? ? AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);

? // 設(shè)置切點表達(dá)式

? AspectJExpressionPointcut ajexp =

? ? new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);

? // PointcutExpression 為注解上value屬性的值

? ajexp.setExpression(aspectJAnnotation.getPointcutExpression());

? if (this.beanFactory != null) {

? ? ajexp.setBeanFactory(this.beanFactory);

? }

? return ajexp;

}

new InstantiationModelAwarePointcutAdvisorImpl令宿,在這里叼耙,才會真正創(chuàng)建出advice

public InstantiationModelAwarePointcutAdvisorImpl(){

? //...省略賦值過程...

? // 實例化出advice

? this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);

}

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {

? // 獲取advice,aspectJAdviceMethod為方法,aspectName為切面類

? Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? this.aspectInstanceFactory, this.declarationOrder, this.aspectName);

? return (advice != null ? advice : EMPTY_ADVICE);

}

public Advice getAdvice(){

? // 根據(jù)方法獲取到注解信息

? AspectJAnnotation<?> aspectJAnnotation =

AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);

? AbstractAspectJAdvice springAdvice;

? // 根據(jù)注解類型返回對象粒没,創(chuàng)建對象的過程都是一樣的筛婉,都是調(diào)用父類的構(gòu)造方法

? // candidateAdviceMethod為切面的方法,expressionPointcut是切點

? switch (aspectJAnnotation.getAnnotationType()) {

? ? case AtPointcut

? ? ? return null;

? ? case AtAround:

? ? ? springAdvice = new AspectJAroundAdvice(

? ? ? ? candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);

? ? ? break;

? ? case AtBefore:

? ? ? springAdvice = new AspectJMethodBeforeAdvice(

? ? ? ? candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);

? ? ? break;

? ? case AtAfter:

? ? ? springAdvice = new AspectJAfterAdvice(

? ? ? ? candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);

? ? ? break;

? ? ? //...省略其他的advice

? ? default:

? ? ? throw new UnsupportedOperationException(

? ? ? ? "Unsupported advice type on method: " + candidateAdviceMethod);

? }

? return springAdvice;

}

springAdvice已創(chuàng)建完畢癞松,意味著切面中的某個方法已經(jīng)解析完畢了爽撒,其他的方法解析過程大致也是相似的

小結(jié)

其實解析切面本身并不復(fù)雜,只是Spring中將切面類封裝來封裝去容易使人混亂响蓉,如buildAspectJAdvisors方法中硕勿,封裝了一個AspectMetadata amd = new AspectMetadata(beanType, beanName);,又立即發(fā)起判定amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON枫甲,其實這里完全可以變?yōu)锳jTypeSystem.getAjType(currClass).getPerClause().getKind() == PerClauseKind.SINGLETON源武,AjTypeSystem.getAjType(currClass)為new AspectMetadata的一部分邏輯,筆者這里給大家總結(jié)一下吧想幻。

首先粱栖,循環(huán)所有的beanName,找到帶有@Aspectj注解的class, 獲取到class中的所有方法進(jìn)行遍歷解析脏毯,取出方法注解上的值(切點:pointcut)闹究,然后把方法,切點表達(dá)式食店,封裝了BeanFactory,BeanName的factory封裝成相應(yīng)的SpringAdvice, 由SpringAdvice和pointcut組合成一個advisor渣淤。

創(chuàng)建代理對象

切面已經(jīng)解析完畢,接下來叛买,我們就來看看如何把解析出的切面織入到目標(biāo)方法中吧

但,在這之前蹋订,還有必要給小伙伴們補充一點前置知識率挣。

我們知道,一個bean是否能夠被aop代理露戒,取決于它是否滿足代理條件椒功,即為是否能夠被切點表達(dá)式所命中,而在Spring AOP中智什,bean與切點表達(dá)式進(jìn)行匹配的是AspectJ實現(xiàn)的动漾,并非Spring所完成的,所以我們先來看看AspectJ如何匹配出合適的bean的吧

栗子

首先需要引入org.aspectj:aspectjweaver依賴

一個Service荠锭,包名為com.my.spring.test.aop

package com.my.spring.test.aop;

/**

* 切點表達(dá)式可以匹配的類

*

*/

public class ServiceImpl{

/**

* 切點表達(dá)式可以匹配的方法

*/

? public void doService() {

? ? System.out.println("do service ...");

? }

public void matchMethod() {

System.out.println("ServiceImpl.notMatchMethod");

}

}

然后旱眯,我們自己封裝一個用于匹配的工具類,具體功能大家看注釋哈哈

package com.my.spring.test.aspectj;

import org.aspectj.weaver.tools.PointcutExpression;

import org.aspectj.weaver.tools.PointcutParser;

import org.aspectj.weaver.tools.ShadowMatch;

import java.lang.reflect.Method;

/**

* aop工具

*/

public class AOPUtils {

// AspectJ的固定寫法,獲取一個切點解析器

static PointcutParser parser = PointcutParser

.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(

PointcutParser.getAllSupportedPointcutPrimitives(), ClassLoader.getSystemClassLoader());

// 切點表達(dá)式

private static PointcutExpression pointcutExpression;

/**

* 初始化工具類删豺,我們需要先獲取一個切點表達(dá)式

*

* @param expression 表達(dá)式

*/

public static void init(String expression){

// 解析出一個切點表達(dá)式

pointcutExpression =? parser.parsePointcutExpression(expression);

}

/**

* 第一次篩選共虑,根據(jù)類篩選,也叫做粗篩

*

* @param targetClass 目標(biāo)類

* @return 是否匹配

*/

public static boolean firstMatch(Class<?> targetClass){

? ? // 根據(jù)類篩選

return pointcutExpression.couldMatchJoinPointsInType(targetClass);

}

/**

* 第二次篩選呀页,根據(jù)方法篩選妈拌,也叫做精篩,精篩通過則說明完全匹配

* ps: 也可以使用該方法進(jìn)行精篩蓬蝶,粗篩的目的是提高性能尘分,第一次直接過濾掉不合適的類再慢慢精篩

*

* @param method 方法

* @return 是否匹配

*/

public static boolean lastMatch(Method method){

? ? // 根據(jù)方法篩選

ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);

return shadowMatch.alwaysMatches();

}

}

測試

public class AOPUtilsTest {

public static void main(String[] args) throws NoSuchMethodException {

// 定義表達(dá)式

String expression = "execution(* com.my.spring.test.aop.*.*(..))";

// 初始化工具類

AOPUtils.init(expression);

// 粗篩

boolean firstMatch = AOPUtils.firstMatch(ServiceImpl.class);

if(firstMatch){

System.out.println("第一次篩選通過");

// 正常情況應(yīng)該是獲取所有方法進(jìn)行遍歷,我這里偷懶了~

Method doService = ServiceImpl.class.getDeclaredMethod("doService");

// 精篩

boolean lastMatch = AOPUtils.lastMatch(doService);

if(lastMatch){

System.out.println("第二次篩選通過");

}

else{

System.out.println("第二次篩選未通過");

}

}

else {

System.out.println("第一次篩選未通過");

}

}

}

結(jié)果(就不截圖了丸氛,懷疑的小伙伴可以自己試試~)

第一次篩選通過

第二次篩選通過

當(dāng)我們新建一個類Test,把切點表達(dá)式換成

execution(* com.my.spring.test.aop.Test.*(..))

測試結(jié)果為

第一次篩選未通過

再把切點表達(dá)式換成指定的方法

execution(* com.my.spring.test.aop.*.matchMethod(..))

結(jié)果

第一次篩選通過

第二次篩選未通過

到這里培愁,小伙伴們應(yīng)該明白了AspectJ的使用方法吧

代理對象創(chuàng)建過程

接下來,我們就來看看Spring是如何使用AspectJ匹配出相應(yīng)的advisor并創(chuàng)建代理對象的吧雪位,以下為創(chuàng)建代理對象的大致路程圖

創(chuàng)建代理對象是在bean初始化后完成的竭钝,所以對應(yīng)的beanPostProcessor調(diào)用時機為postProcessAfterInitialization

AbstractAutoProxyCreator#postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {

if (bean != null) {

// 獲取緩存key值,其實就是beanName

Object cacheKey = getCacheKey(bean.getClass(), beanName);

// 判斷緩存中是否有該對象雹洗,有則說明該對象已被動態(tài)代理香罐,跳過

if (this.earlyProxyReferences.remove(cacheKey) != bean) {

return wrapIfNecessary(bean, beanName, cacheKey);

}

}

return bean;

}

wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

// 根據(jù)bean獲取到匹配的advisor

? Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

? if (specificInterceptors != DO_NOT_PROXY) {

? ? // 創(chuàng)建代理對象

? ? Object proxy = createProxy(

? ? ? bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

? ? return proxy;

? }

? return bean;

}

getAdvicesAndAdvisorsForBean

protected Object[] getAdvicesAndAdvisorsForBean(

Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

? // 獲取合適的advisor

? List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);

? return advisors.toArray();

}

findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {

? // 先獲取到所有的advisor, 這里和解析過程相同,由于已經(jīng)解析好时肿,所以會直接從緩存中取出

? List<Advisor> candidateAdvisors = findCandidateAdvisors();

? // 篩選出匹配的advisor

? List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

? // 增加一個默認(rèn)的advisor

? extendAdvisors(eligibleAdvisors);

? if (!eligibleAdvisors.isEmpty()) {

? ? // 排序

? ? eligibleAdvisors = sortAdvisors(eligibleAdvisors);

? }

? return eligibleAdvisors;

}

findAdvisorsThatCanApply

protected List<Advisor> findAdvisorsThatCanApply(

List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

? // 查找匹配的advisor

? return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);

}

findAdvisorsThatCanApply

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz){

? List<Advisor> eligibleAdvisors = new ArrayList<>();

? for (Advisor candidate : candidateAdvisors) {

? ? // 判斷是否匹配

? ? if (canApply(candidate, clazz, hasIntroductions)) {

? ? ? // 加入到合適的advisors集合中

? ? ? eligibleAdvisors.add(candidate);

? ? }

? }

? return eligibleAdvisors;

}

canApply

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {

? if (advisor instanceof PointcutAdvisor) {

? ? PointcutAdvisor pca = (PointcutAdvisor) advisor;

? ? // 判斷是否匹配

? ? return canApply(pca.getPointcut(), targetClass, hasIntroductions);

? }

? else {

? ? // It doesn't have a pointcut so we assume it applies.

? ? return true;

? }

}

canApply

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {

// 第一次篩選庇茫,對class篩選判斷是否滿足匹配條件

? // 這里將會初始化切點表達(dá)式

? if (!pc.getClassFilter().matches(targetClass)) {

? ? return false;

? }


? IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;

? if (methodMatcher instanceof IntroductionAwareMethodMatcher) {

? ? introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;

? }


? for (Class<?> clazz : classes) {

? ? Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);

? ? // 循環(huán)所有方法進(jìn)行第二次篩選,判斷是否有方法滿足匹配條件

? ? for (Method method : methods) {

? ? ? if (introductionAwareMethodMatcher != null ?

? ? ? ? ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :

? ? ? ? ? methodMatcher.matches(method, targetClass)) {

? ? ? ? return true;

? ? ? }

? ? }

? }

? return false;

}

pc.getClassFilter()

public ClassFilter getClassFilter() {

? obtainPointcutExpression();

? return this;

}

obtainPointcutExpression

private PointcutExpression obtainPointcutExpression() {

? if (this.pointcutExpression == null) {

? ? // 確認(rèn)類加載器

? ? this.pointcutClassLoader = determinePointcutClassLoader();

? ? // 創(chuàng)建切點表達(dá)式

? ? this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);

? }

? return this.pointcutExpression;

}

buildPointcutExpression

private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {

? // 初始化切點解析器

? PointcutParser parser = initializePointcutParser(classLoader);

? PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];

? for (int i = 0; i < pointcutParameters.length; i++) {

? ? pointcutParameters[i] = parser.createPointcutParameter(

? ? ? this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);

? }

? // 使用切點解析器進(jìn)行解析表達(dá)式獲取切點表達(dá)式

? return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? this.pointcutDeclarationScope, pointcutParameters);

}

initializePointcutParser

private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {

? // 獲得切點解析器

? PointcutParser parser = PointcutParser

? ? .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(

? ? SUPPORTED_PRIMITIVES, classLoader);

? parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());

? return parser;

}

pc.getClassFilter便是完成了以上事情螃成,此時再進(jìn)行調(diào)用matchs方法

public boolean matches(Class<?> targetClass) {

? PointcutExpression pointcutExpression = obtainPointcutExpression();

? // 使用切點表達(dá)式進(jìn)行粗篩

? return pointcutExpression.couldMatchJoinPointsInType(targetClass);

}

introductionAwareMethodMatcher.matches 同樣如此

以上便是尋找合適的advisor的過程旦签,下面,就是通過這些advisor進(jìn)行創(chuàng)建動態(tài)代理了

createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,

@Nullable Object[] specificInterceptors, TargetSource targetSource) {

? ProxyFactory proxyFactory = new ProxyFactory();

? proxyFactory.copyFrom(this);

// 將specificInterceptors(現(xiàn)在是Object)轉(zhuǎn)化為Advisor返回

? Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);

? // 賦值到proxyFactory的advisors屬性中

? proxyFactory.addAdvisors(advisors);

? proxyFactory.setTargetSource(targetSource);

? customizeProxyFactory(proxyFactory);

? // 創(chuàng)建動態(tài)代理

? return proxyFactory.getProxy(getProxyClassLoader());

}

proxyFactory.getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {

? // 創(chuàng)建代理對象

? return createAopProxy().getProxy(classLoader);

}

createAopProxy

protected final synchronized AopProxy createAopProxy() {

? // 創(chuàng)建AOP代理對象

? return getAopProxyFactory().createAopProxy(this);

}

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

? // @EnableAspectJAutoProxy的proxyTargetClass是否配置為true

? 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.");

? ? }

? ? // 如何是接口則創(chuàng)建jdk動態(tài)代理

? ? if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {

? ? ? return new JdkDynamicAopProxy(config);

? ? }

? ? // cglib動態(tài)代理

? ? return new ObjenesisCglibAopProxy(config);

? }

? // 默認(rèn)是jdk動態(tài)代理

? else {

? ? return new JdkDynamicAopProxy(config);

? }

}

public Object getProxy(@Nullable ClassLoader classLoader) {

? // 獲取到代理的接口

? Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);

? findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);

? // 創(chuàng)建jdk代理寸宏,傳入的為JdkDynamicAopProxy對象宁炫,里面包含了被代理的bean以及匹配的advisor

? return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

}

動態(tài)代理創(chuàng)建完成~

代理對象調(diào)用過程

對象都給你創(chuàng)建好了,接下當(dāng)然是開..發(fā)起調(diào)用咯

以下是調(diào)用的大致流程圖

代理對象被調(diào)用的是invoke方法氮凝,我們所創(chuàng)建的代理對象為JdkDynamicAopProxy羔巢,所以

JdkDynamicAopProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

? Object oldProxy = null;

? boolean setProxyContext = false;

? // 取出包裝了被代理bean的對象->創(chuàng)建代理對象時的SingletonTargetSource, advised為ProxyFactory

? TargetSource targetSource = this.advised.targetSource;

? Object target = null;

? // 拿到bean

? target = targetSource.getTarget();

? Class<?> targetClass = (target != null ? target.getClass() : null);

? // 將所有advisor中的advice取出,并轉(zhuǎn)化為對應(yīng)的interceptor

? List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

? // 創(chuàng)建一個最外層的MethodInvocation用于發(fā)起調(diào)用

? MethodInvocation invocation =

? ? new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

? // 發(fā)起鏈?zhǔn)秸{(diào)用

? Object retVal = invocation.proceed();

? return retVal;

}

我們先看獲取interceptor的過程

getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {

? // 將所有advisor中的advice取出并封裝成intercept

? return this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);

}

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(

? Advised config, Method method, @Nullable Class<?> targetClass) {

// 創(chuàng)建一個advisor適配器的注冊器用于轉(zhuǎn)化advice罩阵,創(chuàng)建時將默認(rèn)注冊三個適配器

? AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

? Advisor[] advisors = config.getAdvisors();

? // 循環(huán)遍歷所有advisor

? for (Advisor advisor : advisors) {

? // 將advisor中的advice轉(zhuǎn)化為interceptor

? ? MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

? ? interceptorList.addAll(Arrays.asList(interceptors));

? ? return interceptorList;

? }

}

GlobalAdvisorAdapterRegistry.getInstance() 類初始化時調(diào)用靜態(tài)方法

private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry()

public static AdvisorAdapterRegistry getInstance() {

return instance;

}

public DefaultAdvisorAdapterRegistry() {

? // 注冊三個適配器

? registerAdvisorAdapter(new MethodBeforeAdviceAdapter());

? registerAdvisorAdapter(new AfterReturningAdviceAdapter());

? registerAdvisorAdapter(new ThrowsAdviceAdapter());

}

public void registerAdvisorAdapter(AdvisorAdapter adapter) {

? // 將適配器加入集合

? this.adapters.add(adapter);

}

registry.getInterceptors 這里面包含了advice轉(zhuǎn)化成interceptor的過程

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {

? List<MethodInterceptor> interceptors = new ArrayList<>(3);

? Advice advice = advisor.getAdvice();

? // advice本身是否就是MethodInterceptor

? if (advice instanceof MethodInterceptor) {

? ? interceptors.add((MethodInterceptor) advice);

? }

? for (AdvisorAdapter adapter : this.adapters) {

? ? // 判斷advice是哪個advice 如:(advice instanceof MethodBeforeAdvice)

? ? if (adapter.supportsAdvice(advice)) {

? ? ? // 將advice封裝到對應(yīng)的interceptor

? ? ? interceptors.add(adapter.getInterceptor(advisor));

? ? }

? }

? return interceptors.toArray(new MethodInterceptor[0]);

}

若adapter為MethodBeforeAdviceAdapter竿秆,則

public MethodInterceptor getInterceptor(Advisor advisor) {

? MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();

? return new MethodBeforeAdviceInterceptor(advice);

}

其他advice轉(zhuǎn)化過程相同

以上,便將所有的advice轉(zhuǎn)化成了interceptor稿壁,接下來幽钢,則是經(jīng)典的鏈?zhǔn)竭f歸調(diào)用過程

以下過程小伙伴們可以對照流程圖閱讀,畢竟遞歸還是有些復(fù)雜傅是,需要一定的功底

ReflectiveMethodInvocation#proceed

public Object proceed() throws Throwable {

? // currentInterceptorIndex 初始值為-1

? // 當(dāng)currentInterceptorIndex等于advice的數(shù)量減一時匪燕,則調(diào)用目標(biāo)方法

? // 由于advice已排好序蕾羊,所以調(diào)用順序為before, after, afterReturn, afterThrowing

? // 注意,并非調(diào)用到相應(yīng)的advice就會執(zhí)行advice方法谎懦,這里是類似遞歸調(diào)用的方式肚豺,會存在一個歸過程

? // 有些是遞的時候發(fā)起調(diào)用,如beforeAdvice, 但有些則是歸的時候發(fā)起調(diào)用界拦,如afterAdvice

? // 遞歸的終止條件則是這下面這個return invokeJoinpoint();

? if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {

? ? return invokeJoinpoint();

? }

// currentInterceptorIndex自增并獲取到interceptor

? Object interceptorOrInterceptionAdvice =

? ? this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

? // 將interceptro強轉(zhuǎn)為MethodInterceptor發(fā)起調(diào)用

? return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

}

此時currentInterceptorIndex值為0吸申,而我們的advice為4個(去除了默認(rèn)的),所以當(dāng)currentInterceptorIndex為3時便會調(diào)用我們的實際方法

首先調(diào)用的是MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {

? // 調(diào)用前置通知

? this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());

? return mi.proceed();

}

mi為傳入的this享甸,所有mi.proceed()將會回到最開始的方法

再次循環(huán)截碴,此時currentInterceptorIndex值為1

調(diào)用的是AspectJAfterAdvice

public Object invoke(MethodInvocation mi) throws Throwable {

? try {

? ? return mi.proceed();

? }

? finally {

? ? // finally意味著不管怎樣都會被調(diào)用

? ? invokeAdviceMethod(getJoinPointMatch(), null, null);

? }

}

繼續(xù),此時currentInterceptorIndex值為2

調(diào)用的是AfterReturningAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {

? Object retVal = mi.proceed();

? this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());

? return retVal;

}

繼續(xù)蛉威,此時currentInterceptorIndex值為3

調(diào)用的是AspectJAfterThrowingAdvice

public Object invoke(MethodInvocation mi) throws Throwable {

? try {

? ? return mi.proceed();

? }

? catch (Throwable ex) {

? ? if (shouldInvokeOnThrowing(ex)) {

? ? ? // 調(diào)用異常通知

? ? ? invokeAdviceMethod(getJoinPointMatch(), null, ex);

? ? }

? ? // 往外拋出異常

? ? throw ex;

? }

}

龍華大道1號 http://www.kinghill.cn/Dynamics/2106.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末日丹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蚯嫌,更是在濱河造成了極大的恐慌哲虾,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件择示,死亡現(xiàn)場離奇詭異束凑,居然都是意外死亡,警方通過查閱死者的電腦和手機栅盲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門汪诉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谈秫,你說我怎么就攤上這事扒寄。” “怎么了拟烫?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵该编,是天一觀的道長。 經(jīng)常有香客問我硕淑,道長课竣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任喜颁,我火速辦了婚禮稠氮,結(jié)果婚禮上曹阔,老公的妹妹穿的比我還像新娘半开。我一直安慰自己,他們只是感情好赃份,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布寂拆。 她就那樣靜靜地躺著奢米,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纠永。 梳的紋絲不亂的頭發(fā)上鬓长,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機與錄音尝江,去河邊找鬼涉波。 笑死,一個胖子當(dāng)著我的面吹牛炭序,可吹牛的內(nèi)容都是我干的啤覆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼惭聂,長吁一口氣:“原來是場噩夢啊……” “哼窗声!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辜纲,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤笨觅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后耕腾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體见剩,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年幽邓,在試婚紗的時候發(fā)現(xiàn)自己被綠了炮温。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡牵舵,死狀恐怖柒啤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情畸颅,我是刑警寧澤担巩,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站没炒,受9級特大地震影響涛癌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜送火,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一拳话、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧种吸,春花似錦弃衍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岸裙。三九已至,卻和暖如春速缆,著一層夾襖步出監(jiān)牢的瞬間降允,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工艺糜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剧董,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓破停,卻偏偏與公主長得像送滞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辱挥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容