逐行閱讀Spring5.X源碼(十二)AOP源碼分析,難!面試官都不一定懂!

警告:閱讀此文前務(wù)必先閱讀之前寫的《spring如何解決循環(huán)引用》,本篇文章高度依賴循環(huán)引用。

在循環(huán)依賴中我們講了spring實例化bean的入口,refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean,看doGetBean中的如下代碼

    if (mbd.isSingleton()) {
                    // 實例化bean
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            // 真正的完成bean的創(chuàng)建
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

createBean(beanName, mbd, args);方法真正的完成了bean的實例化,包括循環(huán)依賴、AOP、生命周期等。

AOP是在何時完成的?

所謂的AOP無非就是將bean加強,在bean的方法前后加上其他的方法而已,bean的class在虛擬機啟動的時候就加載到JVM里了,我們不會通過修改class來動態(tài)擴展bean的功能,但是可以新生成一個類(動態(tài)代理類),這個類呢,包含了bean的所有功能,同時又進行了加強,然后將這個動態(tài)代理類實例化,替換掉原有的bean,最后放到spring單例池中。不知道我這毫無文采的大白話有沒有講清楚。

所以,AOP肯定是跟bean的實例化息息相關(guān)的,注意,我說的是實例化,而不是生命周期。也就是說,我們AOP肯定是在bean實例化好了后再進行動態(tài)代理,想想JDK的動態(tài)代理,是需要一個實例化的被代理對象的,在《spring如何解決循環(huán)引用》這篇文章中我們詳細講了實例化及循環(huán)依賴。所以,AOP是在兩個地方完成代理的:

  1. 所有的bean實例化完成,且循環(huán)依賴也完成,緊接著開始AOP動態(tài)代理,前提是你所代理的類沒有被別的類依賴。
  2. 如果你的類被別的類依賴了,那么在依賴獲取的過程中進行AOP動態(tài)代理。
    這是《spring如何解決循環(huán)引用》文章最后三級緩存的總結(jié)

問什么需要二級緩存?為什么需要bean的半成品需要提前暴露一個工廠? AOP就是一個原因吧!我現(xiàn)在有一個實例A,實例B要依賴我,但是A需要被代理,也就是說,A被代理后才能注入給B,那我B現(xiàn)在就要注入你,總不能等整個容器所有bean都實例化好后再來注入吧,講道理也不是不可能,只是spring覺著太麻煩沒那個必要,干脆,在注入的時候就直接AOP吧(后面會將源碼)!講清楚了沒有!

OK,下馬開始講AOP源碼!

AOP的準備工作

先把測試代碼寫出來
定義一個切面

@Component
@Aspect
public class UserAspect {
    @Pointcut("execution(* com.config.aop.AopTest.*(..))")
    public void pintCut(){
        System.out.println("point cut");
    }
    @Before("com.config.aop.UserAspect.pintCut()")
    public void beforeAdvice(){
        System.out.println("before");
    }

}

被代理的業(yè)務(wù)類


@Service
public class AopTest {

    public void aop(){
        System.out.println("This is my aop test");
    }
}
public class SpringTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Config.class);
        context.refresh();
        AopTest aopTest =  context.getBean(AopTest.class);
        System.out.println(aopTest);
        aopTest.aop();
    }
}

隨著項目的進行,我們的業(yè)務(wù)類會越來越多,spring首先要過濾掉哪些類需要代理,哪些不需要。其實,就是判斷類上的注解啦!
跟著上面的源碼createBean(beanName, mbd, args);繼續(xù)走。

再次聲明 !!! 這里不明白上文的源碼的,補習(xí)《spring如何解決循環(huán)引用》,不要放棄。

進入createBean源碼,找到如下代碼

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

看英文翻譯,給Bean后置處理器一個機會,返回一個替代目標對象的代理對象。

注意,此時還沒后面的bean初始化代碼,這里只是做準備工作,做什么準備?繼續(xù)看。

原來,AOP實現(xiàn)是靠后置處理器完成的,本專題以前的博文講了BeanFactoryPostProcessor,而這里是 BeanPostProcessors ,兩者有啥區(qū)別?BeanFactoryPostProcessor后置處理器干預(yù)了BeanDefinition的生成,而BeanPostProcessors 干預(yù)了bean的實例化。

Object bean = resolveBeforeInstantiation(beanName, mbdToUse); 是AOP第一次開始調(diào)用后置處理器,進去看看吧!

直接定位到核心代碼

bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

有些讀者會懷疑,不是說逐行閱讀嗎?怎么最近都是直接定位了!講道理,如果之前的博文你都讀過,非核心的代碼你都能自行分析了,太簡單的沒必要在這列出了,畢竟大家的時間都很寶貴!

進入源碼

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

for循環(huán),拿到所有的BeanPostProcessors。



這么多后置處理器用到哪一個呢,自己調(diào)試去,看我吹沒用。


AnnotationAwareAspectJAutoProxyCreator,就他!F5跟進代碼,代碼很難,簡單講一部分,等講生命周期(更難)的時候還會講

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        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;
        }

        return null;
    }

if (this.advisedBeans.containsKey(cacheKey))查看advisedBeans集合是否包含這個類的名字,如果包含就直接返回null了。

private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);

advisedBeans 就是一個集合,用來保存不需要代理的類。比如我們上面定義的切面本身是不需要被代理的,還有加了@Configuration注解的Config配置類,也是不需要代理的,Config其實已經(jīng)被代理了,之前講過。

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

這行代碼就是判斷我們這個業(yè)務(wù)類是否需要被代理,進入isInfrastructureClass代碼:

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        return (super.isInfrastructureClass(beanClass) ||
                (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
    }

調(diào)用父類的方法

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
                Pointcut.class.isAssignableFrom(beanClass) ||
                Advisor.class.isAssignableFrom(beanClass) ||
                AopInfrastructureBean.class.isAssignableFrom(beanClass);
        if (retVal && logger.isTraceEnabled()) {
            logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
        }
        return retVal;
    }

首先會調(diào)用父類的方法
class1.isAssignableFrom(class2) 判定此 Class1 對象所表示的類或接口與指定的 Class2 參數(shù)所表示的類或接口是否相同,或是否是其超類或超接口。如果是則返回 true;否則返回 false。
Advice、Pointcut、Advisor等是跟切面相關(guān)的,不需要代理。
如果父類方法返回false,繼續(xù)判斷該類是不是一個切面,是切面的話也是不需要被代理的。

shouldSkip(beanClass, beanName)會根據(jù)你的AOP配置,找到

符合條件的放入advisedBeans 集合中,后面會根據(jù)這個集合判斷業(yè)務(wù)類是否需要被代理。

OK,趁熱打鐵,我們看AOP代理實現(xiàn),我們現(xiàn)在跳回createBean源碼,從Object bean = resolveBeforeInstantiation(beanName, mbdToUse);繼續(xù)往后看Object beanInstance = doCreateBean(beanName, mbdToUse, args);這行代碼,進入到方法內(nèi),找到下面代碼

    //處理循環(huán)依賴
    populateBean(beanName, mbd, instanceWrapper);
    //開啟生命周期!?。。。?!
    exposedObject = initializeBean(beanName, exposedObject, mbd);

populateBean完成了bean的實例化及循環(huán)依賴,繼續(xù)看initializeBean方法,這個方法就是開啟了聲明周期,AOP實現(xiàn)也是在這里面,進入看源碼倒數(shù)第二行wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);,這個方法就是完成了AOP代理的實現(xiàn),他是怎么實現(xiàn)的呢???

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

擦!還是后置處理器誒!


進去

進入wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

尼瑪,spring源碼太難了! 我花了好久好久才研究到這里。

if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey)))if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)),這兩行判斷跟上面的一樣,不解釋。

如果這兩個判斷都不成立,下面的代碼就是代理的創(chuàng)建!創(chuàng)建!創(chuàng)建!

createProxy里面創(chuàng)建了代理,具體怎么通過CGLIB創(chuàng)建的?讀者自行查看吧!讀spring源碼一定要動手調(diào)試,光看,沒用!

返回到exposedObject = initializeBean(beanName, exposedObject, mbd);
這個時候,exposedObject 就是代理對象了

繼續(xù)返回,


然后繼續(xù)F6往下調(diào)試代碼

此時,就注冊到spring單例池中的,這個addSingleton方法是在getSingleton函數(shù)里完成的,為啥在這個函數(shù)里咧?在這篇文章里,我一言難盡,看《spring如何解決循環(huán)引用》

AOP,就是這么實現(xiàn)的,調(diào)用過程很復(fù)雜,其實就是靠后置處理器。

還沒完,如果你的要代理的業(yè)務(wù)類被其他類循環(huán)依賴了,那么AOP的生成時機就不同了。

先加上兩個循環(huán)依賴的測試代碼

@Service
public class AopTest {

    @Autowired
    AopTest2 aopTest2;
    public void aop(){
        System.out.println("This is my aop test");
    }
}

@Service
public class AopTest2 {
    @Autowired
    AopTest aopTest;

    public void aop(){
        System.out.println("This is my aop test");
    }
}

refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean,在該方法里的第三行:
Object sharedInstance = getSingleton(beanName); ,我們講過,你的業(yè)務(wù)類實例化后會提前暴露一個工廠類,依賴你的類執(zhí)行到這里時,會先調(diào)用這行代碼,我們分析過這行代碼,這里再分析,完全兩個味道

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//      這個singletonObjects就是微觀層面的IOC容器,循環(huán)創(chuàng)建剛開始時,IOC確實是空的,
//      但是我前面一開始的getBean()方法是存在遞歸調(diào)用現(xiàn)象的
        /**
         * 直接舉2個例子:
         * 第一:假如現(xiàn)在在實例化A,結(jié)果有發(fā)現(xiàn)需要給A注入B,
         * 那Spring是不是得獲得B,怎么獲得呢? 遞歸使用getBean(BName)完成,
         * 第二個例子: A被添加上了@Lazy注解,是懶加載的,但是終究有一個會通過getBean(AName)獲取A,
         * 這是發(fā)現(xiàn)A是實例化需要B,B肯定已經(jīng)實例化完事了,同樣是通過遞歸getBean(BName)實現(xiàn)注入,
         * 在這兩個過程中就是getSingleton()保證不會重復(fù)創(chuàng)建已經(jīng)存在的實例
         */
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

看這行

singletonObject = singletonFactory.getObject();

F5跟進源碼,進入到下面代碼



對吧,就是提前暴露的工廠,F(xiàn)5跟進

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

尼瑪,又是后置處理器


跟進去

如果,有人,能看到這里,應(yīng)該不需要我解釋了。這就是AOP生成的第二個時機,在循環(huán)依賴過程中實現(xiàn)AOP,也是半成品的bean實例化完后為什么要暴露一個工廠的原因,而不是一個簡單的bean對象,因為工行能夠提供方法呀,在方法里我們就能處理這個對象啊,這里的處理單指AOP代理,不知道我講沒有講清楚。


上面講過了,一模一樣的,代碼復(fù)用啦
這里注意Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);方法就是找到切面的,怎么找到的,讀者可以跟進代碼讀一下,就是拿到所有的beanDefinition,然后找所有帶有@Aspect注解的類,因為我們可以定義多個切面,所以繼續(xù)找到我們業(yè)務(wù)類所在的那個切面,也就時遍歷所有的切面,然后根據(jù)@Pointcut("execution(* com.config.aop.AopTest.*(..))")進行匹配。


好吧,不賣關(guān)子了,我們看看getProxy方法吧,跟進去

public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

繼續(xù)跟進createAopProxy方法

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

跟進createAopProxy

    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);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

關(guān)于AOP的配置信息全部在config參數(shù)中,看一下我們最關(guān)心的兩個值,切面和目標對象。


對吧,都有了,那就代理吧!
判斷是否實現(xiàn)了接口,實現(xiàn)接口了就用JDK自帶的動態(tài)代理技術(shù),不然就用CGLIB動態(tài)代理技術(shù)。我們這里顯然用了CGLIB動態(tài)代理技術(shù),然后一直返回到下圖所示

進入getProxy創(chuàng)建代理對象

至此,代理對象就創(chuàng)建完成了,我講的大概的過程,沒有細講每行代碼,感興趣的自行讀吧,我實在寫不下去了,肩膀疼!

我們知道aopTest2,是在populateBean(beanName, mbd, instanceWrapper);完成了屬性注入對吧,注入完成后我們看一下情況

看見沒,aopTest2依賴了aopTest,此時注入的就是代理的aopTest了。

面試中,AOP會經(jīng)常被問到,筆者給別人面試也會問。其實面試官不是想問你AOP的概念和如何使用,他會考察你對spring的了解程度,或者看你是否閱讀過源碼。面試造火箭,工作擰螺絲。確實,工作中很少去看spring源碼,但是現(xiàn)在java程序員泛濫了,你如何脫穎而出,面試官也很為難啊,我知道A和B都會用,但是A讀過源碼,說明他對技術(shù)感興趣,而且肯鉆研,工作會游刃有余!

你面試如果給面試官分析上面的動態(tài)代理過程,差不多能秒殺大部分面試官了!面試官也不見得所有的技術(shù)都去讀源碼的!

文章的最后,給大家留個懸念! Spring中的事務(wù)是怎么回事?原理呢?源碼如何實現(xiàn)?其實也是用到了AOP,你讀懂了這篇文章就很容易解決。 加油

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容