1. 程式人生 > >這一次搞懂Spring代理建立及AOP鏈式呼叫過程

這一次搞懂Spring代理建立及AOP鏈式呼叫過程

@[toc] # 前言 AOP,也就是面向切面程式設計,它可以將公共的程式碼抽離出來,動態的織入到目標類、目標方法中,大大提高我們程式設計的效率,也使程式變得更加優雅。如事務、操作日誌等都可以使用AOP實現。這種織入可以是**在執行期動態生成代理物件**實現,也可以在**編譯期**、**類載入時期**靜態織入到程式碼中。而Spring正是通過第一種方法實現,且在代理類的生成上也有兩種方式:JDK Proxy和CGLIB,預設當類實現了介面時使用前者,否則使用後者;另外Spring AOP只能實現對方法的增強。 # 正文 ## 基本概念 AOP的術語很多,雖然不清楚術語我們也能很熟練地使用AOP,但是要理解分析原始碼,術語就需要深刻體會其含義。 - 增強(Advice):就是我們想要額外增加的功能 - 目標物件(Target):就是我們想要增強的目標類,如果沒有AOP,我們需要在每個目標物件中實現日誌、事務管理等非業務邏輯 - 連線點(JoinPoint):程式執行時的特定時機,如方法執行前、後以及丟擲異常後等等。 - 切點(Pointcut):連線點的導航,我們如何找到目標物件呢?切點的作用就在於此,在Spring中就是匹配表示式。 - 引介(Introduction):引介是一種特殊的增強,它為類新增一些屬性和方法。這樣,即使一個業務類原本沒有實現某個介面,通過AOP的引介功能,我們可以動態地為該業務類新增介面的實現邏輯,讓業務類成為這個介面的實現類。 - 織入(Weaving):即如何將增強新增到目標物件的連線點上,有動態(執行期生成代理)、靜態(編譯期、類載入時期)兩種方式。 - 代理(Proxy):目標物件被織入增強後,就會產生一個代理物件,該物件可能是和原物件實現了同樣的一個介面(JDK),也可能是原物件的子類(CGLIB)。 - 切面(Aspect、Advisor):切面由切點和增強組成,包含了這兩者的定義。 ## 代理物件的建立 在熟悉了AOP術語後,下面就來看看Spring是如何建立代理物件的,是否還記得上一篇提到的AOP的入口呢?在**AbstractAutowireCapableBeanFactory**類的**applyBeanPostProcessorsAfterInitialization**方法中迴圈呼叫了**BeanPostProcessor**的**postProcessAfterInitialization**方法,其中一個就是我們建立代理物件的入口。這裡是Bean例項化完成去建立代理物件,理所當然應該這樣,但實際上在Bean例項化之前呼叫了一個**resolveBeforeInstantiation**方法,這裡實際上我們也是有機會可以提前建立代理物件的,這裡放到最後來分析,先來看主入口,進入到**AbstractAutoProxyCreator**類中: ```java public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //建立當前bean的代理,如果這個bean有advice的話,重點看 // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //如果有切面,則生成該bean的代理 if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //把被代理物件bean例項封裝到SingletonTargetSource物件中 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; } ``` 先從快取中拿,沒有則呼叫**wrapIfNecessary**方法建立。在這個方法裡面主要看兩個地方:**getAdvicesAndAdvisorsForBean**和**createProxy**。簡單一句話概括就是先掃描後建立,問題是掃描什麼呢?你可以先結合上面的概念思考下,換你會怎麼做。進入到子類**AbstractAdvisorAutoProxyCreator**的**getAdvicesAndAdvisorsForBean**方法中: ```java protected Object[] getAdvicesAndAdvisorsForBean( Class beanClass, String beanName, @Nullable TargetSource targetSource) { //找到合格的切