spring原始碼剖析(六)AOP實現原理剖析
Spring的AOP實現原理,醞釀了一些日子,寫部落格之前信心不是很足,所以重新閱讀了一邊AOP的實現核心程式碼,而且又從網上找了一些Spring Aop剖析的例子,但是發現掛羊頭買狗肉的太多,標題高大上,內容卻大部分都是比較淺顯的一些介紹,可能也是由於比較少人閱讀這部分的核心程式碼邏輯把,然後寫這部分介紹的人估計也是少之又少,不過說實話,Spring Aop的核心原理實現介紹確實不太好寫,裡面涉及的類之間的呼叫還是蠻多的,關係圖畫的太細的畫也很難畫,而且最重要的一點就是,如果對AOP的概念以及spring的xml的解析,標籤的解析,註解實現,還有java的代理,這些知識沒有好好的理解的話也不可能對AOP的實現詳細邏輯有一個好的理解。所以的話,建議是把這些前置知識都大概瞭解了,再來看這個AOP的實現,或者去閱讀原始碼,那樣的話學習 起來會容易的多。
學習原始碼的過程比較枯燥,尤其是spring比較嚴謹,呼叫層次比較多,沒有畫時序圖的話可能真的被繞暈,所以建議學的時候還是畫畫時序圖,然後跟著debug模式流程走一遍。
首先我們來熱熱身吧,先看看AOP的簡介,待會再進入主題,
AOP簡介
概念
切面(Aspect) :官方的抽象定義為“一個關注點的模組化,這個關注點可能會橫切多個物件”。
連線點(Joinpoint) :程式執行過程中的某一行為。
通知(Advice) :“切面”對於某個“連線點”所產生的動作。
切入點(Pointcut) :匹配連線點的斷言,在AOP中通知和一個切入點表示式關聯。
目標物件(Target Object) :被一個或者多個切面所通知的物件。
AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。
通知(Advice)型別
前置通知(Before advice) :在某連線點(JoinPoint)之前執行的通知,但這個通知不能阻止連線點前的執行。ApplicationContext中在<aop:aspect>裡面使用<aop:before>元素進行宣告。
後通知(After advice) :當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裡面使用<aop:after>元素進行宣告。
返回後通知(After return advice) :在某連線點正常完成後執行的通知,不包括丟擲異常的情況。ApplicationContext中在<aop:aspect>裡面使用<after-returning>元素進行宣告。
環繞通知(Around advice) :包圍一個連線點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的呼叫前後完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行宣告。
丟擲異常後通知(After throwing advice) : 在方法丟擲異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裡面使用<aop:after-throwing>元素進行宣告。
切入點表示式 :如execution(* com.spring.service.*.*(..))
特點
1、降低模組之間的耦合度
2、使系統容易擴充套件
3、更好的程式碼複用。
動態AOP使用示例
由於在之前的部落格,已經介紹過spring原始碼剖析(五)利用AOP實現自定義Spring註解 裡面有介紹到AOP 的簡單使用,相信大家要看這個AOP的原理的也對AOP的使用比較熟悉了,所以在這裡也不再重複展示了。
動態AOP自定義標籤
我之前的部落格中有說到,如何自定義Spring標籤的,以及自定義Spring標籤的大概解析流程,其實這裡的AOP的標籤的定義也和之前的邏輯類似,先上時序圖把:
時序圖
流程說明
1)AOP標籤的定義解析劉徹骨肯定是從NamespaceHandlerSupport的實現類開始解析的,這個實現類就是AopNamespaceHandler。至於為什麼會是從NamespaceHandlerSupport的實現類開始解析的,這個的話我想讀者可以去在回去看看Spring自定義標籤的解析流程,裡面說的比較詳細。
2)要啟用AOP,我們一般會在Spring裡面配置<aop:aspectj-autoproxy/> ,所以在配置檔案中在遇到aspectj-autoproxy標籤的時候我們會採用AspectJAutoProxyBeanDefinitionParser解析器
3)進入AspectJAutoProxyBeanDefinitionParser解析器後,呼叫AspectJAutoProxyBeanDefinitionParser已覆蓋BeanDefinitionParser的parser方法,然後parser方法把請求轉交給了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去處理
4)進入AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法後,先呼叫AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,裡面在轉發呼叫給registerOrEscalateApcAsRequired,註冊或者升級AnnotationAwareAspectJAutoProxyCreator類。對於AOP的實現,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根據@point註解定義的切點來代理相匹配的bean。
5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法處理完成之後,接下來會呼叫useClassProxyingIfNecessary() 處理proxy-target-class以及expose-proxy屬性。如果將proxy-target-class設定為true的話,那麼會強制使用CGLIB代理,否則使用jdk動態代理,expose-proxy屬性是為了解決有時候目標物件內部的自我呼叫無法實現切面增強。
6)最後的呼叫registerComponentIfNecessary 方法,註冊組建並且通知便於監聽器做進一步處理。
建立AOP代理
上面說到AOP的核心邏輯是在AnnotationAwareAspectJAutoProxyCreator類裡面實現,那麼我們先來看看這個類的層次關係
我們可以看到這個類實現了BeanPostProcessor介面,那就意味著這個類在spring載入例項化前會呼叫postProcessAfterInitialization方法,對於AOP的邏輯也是由此開始的。
時序圖
流程說明
1)spring 容器啟動,每個bean的例項化之前都會先經過AbstractAutoProxyCreator類的postProcessAfterInitialization()這個方法,然後接下來是呼叫wrapIfNecessary方法。
- /**
- * Create a proxy with the configured interceptors if the bean is
- * identified as one to proxy by the subclass.
- * @see #getAdvicesAndAdvisorsForBean
- */
- public Object <strong>postProcessAfterInitialization</strong>(Object bean, String beanName) throws BeansException {
- if (bean != null) {
- Object cacheKey = getCacheKey(bean.getClass(), beanName);
- if (!this.earlyProxyReferences.containsKey(cacheKey)) {
- return wrapIfNecessary(bean, beanName, cacheKey);
- }
- }
- return bean;
- }
2)進入wrapIfNecessary方法後,我們直接看重點實現邏輯的方法getAdvicesAndAdvisorsForBean,這個方法會提取當前bean 的所有增強方法,然後獲取到適合的當前bean 的增強方法,然後對增強方法進行排序,最後返回。
- /**
- * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
- * @param bean the raw bean instance
- * @param beanName the name of the bean
- * @param cacheKey the cache key for metadata access
- * @return a proxy wrapping the bean, or the raw bean instance as-is
- */
- protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
- if (beanName != null && this.targetSourcedBeans.containsKey(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 = <strong>getAdvicesAndAdvisorsForBean</strong>(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;
- }
- /**
- * Create an AOP proxy for the given bean.
- * @param beanClass the class of the bean
- * @param beanName the name of the bean
- * @param specificInterceptors the set of interceptors that is
- * specific to this bean (may be empty, but not null)
- * @param targetSource the TargetSource for the proxy,
- * already pre-configured to access the bean
- * @return the AOP proxy for the bean
- * @see #buildAdvisors
- */
- protected Object createProxy(
- Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
- ProxyFactory proxyFactory = new ProxyFactory();
- // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
- proxyFactory.copyFrom(this);
- if (!shouldProxyTargetClass(beanClass, beanName)) {
- // Must allow for introductions; can't just set interfaces to
- // the target's interfaces only.
- Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
- for (Class<?> targetInterface : targetInterfaces) {
- proxyFactory.addInterface(targetInterface);
- }
- }
- Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
- for (Advisor advisor : advisors) {
- proxyFactory.addAdvisor(advisor);
- }
- proxyFactory.<strong>setTargetSource</strong>(targetSource);
- customizeProxyFactory(proxyFactory);
- proxyFactory.setFrozen(this.freezeProxy);
- if (advisorsPreFiltered()) {
- proxyFactory.setPreFiltered(true);
- }
- return proxyFactory.getProxy(this.proxyClassLoader);
- }
AOP動態代理執行
關於AOP的動態代理執行,有兩種主要的方式JDK的動態代理和CGLIB的動態代理,如果對這兩種代理不是很熟悉的話,建議先去看看我之前寫的一篇部落格 java代理(靜態代理和jdk動態代理以及cglib代理) 裡面有介紹這兩種代理的使用和實現方式。 接下來,我們先來看看AOP動態代理的實現選擇方式,先上核心實現程式碼:- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if<