Aop的執(zhí)行原理,我們應(yīng)該基本都了解:通過編寫切面類掐场,我們可以在指定的切入點(diǎn)處插入我們額外的代碼塊往扔,就好比代理模式中贩猎,我們可以在執(zhí)行目標(biāo)方法的前后干一些自己想干的事情。那么這是怎么樣實(shí)現(xiàn)的呢萍膛?
我們自己寫的類中的代碼是硬編碼寫死的吭服,要想改變一個已經(jīng)寫好的類,我們常見的操作就是動態(tài)代理了蝗罗,沒錯艇棕,AOP的底層就是將切入點(diǎn)所在的類創(chuàng)建成了代理對象。
我們知道Spring中的一個主要功能就是管理所有的bean對象串塑,在創(chuàng)建對象的時候沼琉,為我們提供了很多的擴(kuò)展點(diǎn),可以方便我們來干預(yù)對象的創(chuàng)建桩匪,那么Aop究竟是在哪一個擴(kuò)展點(diǎn)的地方幫我們創(chuàng)建了代理對象呢刺桃?本文就來講解下從Aop的前置準(zhǔn)備到創(chuàng)建代理對象的整個流程。
一吸祟、@EnableAspectJAutoProxy
熟悉Spring開發(fā)的模式的話瑟慈,我們都知道,開啟一個新的功能的話屋匕,我們基本需要在配置類上加上一個@EnableXxx的注解葛碧,而這類@EnableXxx的注解多半是向Spring容器中注入了影響bean創(chuàng)建生命周期的bean信息,開啟基于注解的Aop的@EnableAspectJAutoProxy注解同樣于此,他為我們導(dǎo)入了AnnotationAwareAspectJAutoProxyCreator類的定義信息过吻。我們來看下AnnotationAwareAspectJAutoProxyCreator的繼承實(shí)現(xiàn)結(jié)構(gòu)圖:
在整個結(jié)構(gòu)圖中进泼,能干預(yù)bean的生命周期的是左上角的BeanPostProcessor接口,那么整個AOP功能的具體實(shí)現(xiàn)就是在對于InstantiationAwareBeanPostProcessor的方法的具體實(shí)現(xiàn)中纤虽,就是AbstractAutoProxyCreator類中postProcessBeforeInstantiation方法和postProcessAfterInitialization方法乳绕,我們下面就關(guān)注它的具體實(shí)現(xiàn):
二、Aop的前置準(zhǔn)備
1逼纸、AOP基礎(chǔ)準(zhǔn)備工作
首先洋措,我們來看AbstractAutoProxyCreator類中postProcessBeforeInstantiation方法的具體實(shí)現(xiàn),他實(shí)現(xiàn)的接口是InstantiationAwareBeanPostProcessor杰刽,而InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation的觸發(fā)時機(jī)是在Spring容器開始準(zhǔn)備創(chuàng)建bean之前菠发,Spring給我們提供了一個機(jī)會可以自己在這創(chuàng)建對象,而不走Spring的創(chuàng)建對象流程贺嫂,但是AOP會在此就創(chuàng)建對象嗎滓鸠?我們繼續(xù)往下看。
我們先看下前半部分代碼:
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {//advisedBeans已經(jīng)分析過的組件第喳,不是增強(qiáng)過的組件
return null;
}//所有增強(qiáng)了的組件會被緩存在advisedBeans中糜俗,如果是我們需要增強(qiáng)的bean,就放在緩存中
//isInfrastructureClass(beanClass):判斷當(dāng)前類是否有@Aspect注解,即當(dāng)前類是否是切面;shouldSkip中解析了所有的切面類并封裝了切面類中的切面方法
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {// shouldSkip:如果是切面類悠抹,則跳過處
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
這段代碼中主要在管理advisedBeans這個集合珠月,這個集合中key為bean的名稱,value為boolean值锌钮,個人理解為這個集合的意義是桥温,key表示處理過的beanName,而value值表示這個bean應(yīng)不應(yīng)該被增強(qiáng)【不代理】梁丘。那什么樣的類是不需要增強(qiáng)的呢侵浸?這就需要關(guān)注if中的兩個條件了,一個是isInfrastructureClass(beanClass)氛谜,另一個是shouldSkip(beanClass, beanName)掏觉,滿足這兩個條件之一的都會加入advisedBeans集合,并且標(biāo)記為不增強(qiáng)值漫;一起來看看這兩個方法:
- isInfrastructureClass(beanClass):判斷是不是AOP的基礎(chǔ)設(shè)施類澳腹,如果是的話,就加入到advised集合中杨何,并標(biāo)記為不應(yīng)該增強(qiáng)酱塔,那么究竟什么類是AOP的基礎(chǔ)設(shè)施類呢?深入到代碼里我們發(fā)現(xiàn):分為兩大塊:一個是實(shí)現(xiàn)了Advice危虱,Pointcut羊娃,Advisor,AopInfrastructureBean這四個接口的類屬于AOP的基礎(chǔ)設(shè)施類埃跷;另一個是這個類上面標(biāo)注了@Aspect,即切面類
- shouldSkip(beanClass, beanName):這個方法是AOP準(zhǔn)備過程中的一個重要點(diǎn)蕊玷,因?yàn)檫@個方法中干了很多事情,為后面的AOP代理做好了鋪墊弥雹;代碼如下:
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
整個應(yīng)不應(yīng)該跳過的邏輯是:獲取所有的Advisor集合垃帅,然后遍歷Advisor集合,如果當(dāng)前bean的名稱和其中的一個AspectJPointcutAdvisor的aspectName相同剪勿,其實(shí)就是說明這個類是切面類贸诚,那么就會跳過。到這里窗宦,我相信有的小伙伴就會發(fā)出一個疑問了:前面不是處理過了嗎赦颇?為什么此處還是需要處理呢?其實(shí)這里我也有點(diǎn)疑問赴涵,但是它干了更多的事情,那就是創(chuàng)建并緩存了所有的Advisor對象订讼。
在findCandidateAdvisors方法的邏輯中首先是super執(zhí)行了AbstractAdvisorAutoProxyCreator中的邏輯:找到所有的Advisor的實(shí)現(xiàn)類的bean名稱髓窜,并進(jìn)行g(shù)etBean創(chuàng)建對象;其次時尋找@Aspect注解的類,然后為通知方法構(gòu)建Advisor,整個的構(gòu)建流程可以歸納如下:
- 拿到容器中所有的bean名稱
- 循環(huán)遍歷beanName,拿到beanType,判斷是不是切面類(@Aspect)
- 是切面類的話寄纵,就拿到類中除去標(biāo)有@Pointcut的方法鳖敷。然后遍歷方法:
- 把每個切面方法構(gòu)建為Advisor【InstantiationModelAwarePointcutAdvisorImpl】
- 處理@DeclareParents屬性注解,最終會構(gòu)建DeclareParentsAdvisor【屬于IntroductionAdvisor程拭,類級別的切入】定踱,其中對應(yīng)的Advice為DelegatePerTargetObjectIntroductionInterceptor【屬于IntroductionInterceptor,同樣也是MethodInterceptor】
- 緩存進(jìn)advisorsCache 【beanName--->List<Advisor>】
2恃鞋、Advice的構(gòu)建
其中崖媚,在構(gòu)建InstantiationModelAwarePointcutAdvisorImpl中,會構(gòu)建當(dāng)前增強(qiáng)方法的Advice,也就是構(gòu)造方法中的instantiateAdvice方法恤浪,最終會調(diào)用ReflectiveAspectJAdvisorFactory的getAdvice方法來構(gòu)建相應(yīng)的Advice,關(guān)鍵代碼如下:
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
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;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
最終會把切面類beanName和其中的增強(qiáng)方法Advisor存放到advisorsCache緩存中畅哑,方便后面使用。
可以說準(zhǔn)備階段水由,Spring主要就干了兩件事吧:第一件事是標(biāo)記那些不需要代理的AOP基礎(chǔ)設(shè)施類荠呐;第二件事就是尋找并創(chuàng)建容器中所有的Advisor,這一步也分為兩小步:其一砂客,去尋找并創(chuàng)建所有直接實(shí)現(xiàn)了Advisor接口的泥张;其二,處理標(biāo)注了@Aspect注解的類鞠值,并將其以aspectName及List<Advisor>的形式緩存到advisorsCache中媚创。
3、Advisor & Advice
有必要介紹下AOP中的Advisor和Advice,這兩個東西到底有什么區(qū)別和關(guān)系呢齿诉?Advice就是我們要干預(yù)正常代碼而額外加的一部分代碼邏輯筝野,其實(shí)可以理解為攔截器,而Advisor可以說成是對Advice的一種封裝吧粤剧,因?yàn)槊總€Advisor包含一個Advice歇竟,然后Advisor還應(yīng)該包括Advice增強(qiáng)的增強(qiáng)表達(dá)式,即它應(yīng)該什么時候進(jìn)行增強(qiáng)抵恋,即增強(qiáng)條件吧焕议;在Spring中,提供了兩個Advisor的子接口弧关,分別是IntroductionAdvisor和PointcutAdvisor盅安,IntroductionAdvisor提供的getClassFilter()方法和PointcutAdvisor中提供的getPointcut()方法就是各自的增強(qiáng)條件(如下圖),從增強(qiáng)條件我們也可以看出:IntroductionAdvisor是基于類級別的增強(qiáng)世囊,而PointcutAdvisor是基于方法級別或者類級別的增強(qiáng)别瞭,顯然后者方法級別的增強(qiáng)是更加細(xì)粒度的,也是我們常用的@Aspect注解的Advisor株憾。如果這么說還是過于抽象的話蝙寨,那我們拿我們常用的切面類@Aspect來進(jìn)行來進(jìn)行類比:
@Component //切面也是容器中的組件
@Aspect //說明這是切面
public class LogAspect {
public LogAspect() {
System.out.println("LogAspect....");
}
@DeclareParents(value = "com.spring.aop.HelloService", defaultImpl = DeclareParentsTestImpl.class)
private DeclareParentsTest declareParentsTest;
//前置通知
@Before("execution(* com.spring.aop.HelloService.sayHello(..))")
public void logStart(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("logStart()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
}
//返回通知
@AfterReturning(value = "execution(* com.spring.aop.HelloService.sayHello(..))",returning = "result")
public void logReturn(JoinPoint joinPoint,Object result) {
String name = joinPoint.getSignature().getName();
System.out.println("logReturn()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+result+"】");
}
//后置通知
@After("execution(* com.spring.aop.HelloService.sayHello(..))")
public void logEnd(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("logEnd()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
}
//異常通知
@AfterThrowing(value = "execution(* com.spring.aop.HelloService.sayHello(..))",throwing = "e")
public void logError(JoinPoint joinPoint,Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println("logError()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+e+"】");
}
}
像上面logStart,logReturn,logEnd,logError等就可以理解為一個Advice,而@Before晒衩,@AfterReturning,@After墙歪,@AfterThrowing中的表達(dá)式就會說一個Pointcut听系,然后方法加上注解中的表達(dá)式就構(gòu)成了一個Advisor(PointcutAdvisor),相信這么說,大家應(yīng)該都能明白了吧虹菲。
三靠胜、Aop生成代理
Spring提供了兩個地方來生成Aop代理對象,下面我們來看看在哪兩個地方可以生成Aop的代理對象:
1毕源、Aop生成代理對象的第一個地方
第一個地方是在AbstractAutoProxyCreator的postProcessBeforeInstantiation方法中浪漠,Aop的準(zhǔn)備工作做完后,就會查看有沒有自定義的TargetSource脑豹,如果有符合的TargetSource的話就會在此處直接創(chuàng)建bean的代理對象郑藏,不會繼續(xù)走Spring的創(chuàng)建bean的流程,但是瘩欺,這個地方有個麻煩的點(diǎn)必盖,就是我們需要干預(yù)到AnnotationAwareAspectJAutoProxyCreator的創(chuàng)建,需要修改其Bean的定義信息俱饿,將我們自定義的TargetSourceCreator賦值給AnnotationAwareAspectJAutoProxyCreator的customTargetSourceCreators屬性歌粥,我們可以如下操作:
自定義的TargetSource:
public class MyTargetSource implements TargetSource {
private final Object target;
public MyTargetSource(Object target) {
this.target = target;
}
@Override
public Class<?> getTargetClass() {
return HelloService.class;
}
@Override
public boolean isStatic() {
return true;
}
@Override
public Object getTarget() throws Exception {
return target;
}
@Override
public void releaseTarget(Object target) throws Exception {
}
}
封裝成TargetSourceCreator:
public class MyTargetSourceCreator implements TargetSourceCreator {
@Override
public TargetSource getTargetSource(Class<?> beanClass, String beanName) {
if (beanName.equals("helloService")) {
try {
return new MyTargetSource(ReflectionUtils.accessibleConstructor(HelloService.class).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
實(shí)現(xiàn)BeanFactoryPostProcessor修改AnnotationAwareAspectJAutoProxyCreator的bean定義信息:
@Component
public class TargetSourceCreatorBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); // AnnotationAwareAspectJAutoProxyCreator的beanName
bd.getPropertyValues().add("customTargetSourceCreators",new TargetSourceCreator[]{new MyTargetSourceCreator()});
}
}
如上述配置后,HelloService的對象創(chuàng)建就會在AOP準(zhǔn)備工作做好后拍埠,通過如下代碼失驶,獲取到自定義TargetSource后直接創(chuàng)建AOP代理對象,不走這個bean后面的生命周期了:
//創(chuàng)建個代理,如果為這個類指定了targetSource會在此就生成代理直接返回了枣购,不走這個bean后面的生命周期了
TargetSource targetSource = getCustomTargetSource(beanClass, beanName); //
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
2嬉探、Aop生成代理對象的第二個地方
另一種情況是,當(dāng)沒有為對象指定自定義的TargetSource時棉圈,Spring會在bean的對象創(chuàng)建完成后的AbstractAutoProxyCreator的postProcessAfterInitialization方法中的wrapIfNecessary方法中創(chuàng)建Aop代理對象涩堤,代碼如下:
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(//創(chuàng)建代理對象,specificInterceptors所有的增強(qiáng)器
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
兩處創(chuàng)建Aop代理對象的邏輯都是一樣的分瘾,僅僅是時機(jī)不一樣罷了胎围,Spring默認(rèn)使用的是CGLIB來創(chuàng)建代理對象的,具體的創(chuàng)建過程在此就不說了德召,后續(xù)有時間再補(bǔ)上白魂,我們需要知道的是:當(dāng)我們執(zhí)行被AOP增強(qiáng)的類時,需要回調(diào)DynamicAdvisedInterceptor這個類中的intercept方法上岗,這也是后面我們講述AOP執(zhí)行流程的入口福荸。