你知道Spring是怎麼將AOP應用到Bean的生命週期中的嗎?

程式設計師DMZ發表於2020-07-05

聊一聊Spring是怎麼將AOP應用到Bean的生命週期中的?

本系列文章:

聽說你還沒學Spring就被原始碼編譯勸退了?30+張圖帶你玩轉Spring編譯

讀原始碼,我們可以從第一行讀起

你知道Spring是怎麼解析配置類的嗎?

配置類為什麼要新增@Configuration註解?

談談Spring中的物件跟Bean,你知道Spring怎麼建立物件的嗎?

這篇文章,我們來談一談Spring中的屬性注入

Spring中AOP相關的API及原始碼解析,原來AOP是這樣子的

推薦閱讀:

Spring官網閱讀 | 總結篇

Spring雜談

本系列文章將會帶你一行行的將Spring的原始碼吃透,推薦閱讀的文章是閱讀原始碼的基礎!

前言

在上篇文章中(Spring中AOP相關的API及原始碼解析,原來AOP是這樣子的)我們已經分析過了AOP的實現的原始碼,那麼Spring是如何將AOP應用到Bean的生命週期的呢?這篇文章就帶著大家來探究下這個問題。本文我們要分析的程式碼還是位於org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean這個方法中,在《我們來談一談Spring中的屬性注入 》這篇文章中,我們已經分析過了populateBean這個方法,

image-20200703202825887

所以本文我們接著來看看initializeBean這個方法,它主要乾了這麼幾件事

  1. 執行Aware介面中的方法
  2. 執行生命週期回撥方法
  3. 完成AOP代理

對應原始碼如下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
           
            // 執行Aware介面中的方法
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
            
            // 呼叫InitDestroyAnnotationBeanPostProcessor
            // 的postProcessBeforeInitialization方法
            // 處理@PostContructor註解標註的方法
            // 另外有一部分aware方法也是在這裡呼叫的
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
            // 如果實現了InitializingBean,會呼叫afterPropertiesSet方法
            // 如果XML中配置了init-method屬性,會呼叫對應的初始化方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
            // 在這裡完成AOP
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

因為在Spring官網閱讀(九)Spring中Bean的生命週期(上)文章中我們已經對這個方法做過分析了,並且這個方法本身也比較簡單,所以不再對這個方法做過多贅述,我們主要關注的就是Spring是如何將AOP應用到Bean的生命週期中的,對應的就是applyBeanPostProcessorsAfterInitialization這個方法,其原始碼如下:

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;
}

實際上就是呼叫了所有後置處理器的postProcessAfterInitialization方法,在Spring中AOP相關的API及原始碼解析,原來AOP是這樣子的一文中已經提到過了,@EnableAspectJAutoProxy註解實際上就是向容器中註冊了一個AnnotationAwareAspectJAutoProxyCreator,這個類本身就是一個後置處理器,AOP代理就是由它在這一步完成的。

Bean生命週期中AOP的流程

1、@EnableAspectJAutoProxy

通過@EnableAspectJAutoProxy註解向容器中註冊一個AnnotationAwareAspectJAutoProxyCreatorBeanDefinition,它本身也是一個BeanPostProcessor,這個BeanDefinition會在org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors這個方法中完成建立,如下圖所示

image-20200704112937846

2、postProcessBeforeInstantiation方法執行

執行AnnotationAwareAspectJAutoProxyCreatorpostProcessBeforeInstantiation方法,實際上就是父類AbstractAutoProxyCreatorpostProcessBeforeInstantiation被執行

對應原始碼如下:

//  這個方法的主要目的就是在不考慮通知的情況下,確認哪些Bean不需要被代理
//  1.Advice,Advisor,Pointcut型別的Bean不需要被代理
//  2.不是原始Bean被包裝過的Bean不需要被代理,例如ScopedProxyFactoryBean
//  實際上並不只是這些Bean不需要被代理,如果沒有對應的通知需要被應用到這個Bean上的話
//  這個Bean也是不需要被代理的,只不過不是在這個方法中處理的。
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
    // 如果beanName為空或者為這個bean提供了定製的targetSource
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // advisedBeans是一個map,其中key是BeanName,value代表了這個Bean是否需要被代理
        // 如果已經包含了這個key,不需要在進行判斷了,直接返回即可
        // 因為這個方法的目的就是在例項化前就確認哪些Bean是不需要進行AOP的
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // 說明還沒有對這個Bean進行處理
        // 在這裡會對SpringAOP中的基礎設施bean,例如Advice,Pointcut,Advisor做標記
        // 標誌它們不需要被代理,對應的就是將其放入到advisedBeans中,value設定為false
        // 其次,如果這個Bean不是最原始的Bean,那麼也不進行代理,也將其value設定為false
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // 是否為這個Bean提供了定製的TargetSource
    // 如果提供了定製的TargetSource,那麼直接在這一步建立一個代理物件並返回
    // 一般不會提供
    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;
}

3、postProcessAfterInitialization方法執行

實際上也是執行父類AbstractAutoProxyCreator中的方法,對應原始碼如下:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 什麼時候這個判斷會成立呢?
        // 如果不出現迴圈引用的話,remove方法必定返回null
        // 所以這個remove(cacheKey) != bean肯定會成立
        // 如果發生迴圈依賴的話,這個判斷就不會成立
        // 這個我們在介紹迴圈依賴的時候再詳細分析,
       	// 目前你只需要知道wrapIfNecessary完成了AOP代理
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 需要代理的話,在這裡完成的代理
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

4、wrapIfNecessary方法執行

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   
    // 在postProcessBeforeInstantiation方法中可能已經完成過代理了
    // 如果已經完成代理了,那麼直接返回這個代理的物件
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    
    // 在postProcessBeforeInstantiation方法中可能已經將其標記為不需要代理了
    // 這種情況下,也直接返回這個Bean
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    
    // 跟在postProcessBeforeInstantiation方法中的邏輯一樣
    // 如果不需要代理,直接返回,同時在advisedBeans中標記成false
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 獲取可以應用到這個Bean上的通知
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 如果存在通知的話,說明需要被代理
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 到這裡建立代理,實際上底層就是new了一個ProxyFactory來建立代理的
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
	// 如果沒有通知的話,也將這個Bean標記為不需要代理
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

關於建立代理的具體原始碼分析,在Spring中AOP相關的API及原始碼解析,原來AOP是這樣子的一文中已經做了詳細介紹,所以本文不再贅述,現在我們的重點將放在Spring是如何解析出來通知的,對應方法就是getAdvicesAndAdvisorsForBean,其原始碼如下:

第一步:呼叫org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
  
   // 通過findEligibleAdvisors方法返回對應的通知
   // 這個方法回返回所有能應用在指定的Bean上的通知
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}

第二步:呼叫org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 獲取到所有的通知
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 從獲取到的通知中篩選出能應用到這個Bean上的通知
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

第三步:呼叫org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors獲取到所有的通知

// 這個方法的目的就是為了獲取到所有的通知
protected List<Advisor> findCandidateAdvisors() {
   
    // 先呼叫父類的方法,父類會去查詢容器中所有屬於Advisor型別的Bean
    List<Advisor> advisors = super.findCandidateAdvisors();
    
  	// 這個類本身會通過一個aspectJAdvisorsBuilder來構建通知
    // 構建的邏輯就是解析@Aspect註解所標註的類中的方法
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    
    // 最後返回這些通知
    return advisors;
}

第四步:org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors構建通知,這個方法比較長,我們就只分析其中的關鍵程式碼即可

public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();
					// 會獲取到容器中的所有BeanName
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					for (String beanName : beanNames) {
						// 如果對beanName配置了正則匹配的話,那麼要按照正規表示式的匹配規則進行過濾
						// 預設是沒有的,可以認為isEligibleBean始終返回true
						if (!isEligibleBean(beanName)) {
							continue;
						}
						// We must be careful not to instantiate beans eagerly as in this case they
						// would be cached by the Spring container but would not have been weaved.
						Class<?> beanType = this.beanFactory.getType(beanName);
						if (beanType == null) {
							continue;
						}
						// 判斷類上是否新增了@Aspect註解
						if (this.advisorFactory.isAspect(beanType)) {
							aspectNames.add(beanName);
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							// 預設就是SINGLETON,代理切面物件是單例的
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            // 最後從這個切面例項中解析出所有的通知
                            // 關於通知解析的具體程式碼就不再分析了
 								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								if (this.beanFactory.isSingleton(beanName)) {
									this.advisorsCache.put(beanName, classAdvisors);
								}
								else {
									this.aspectFactoryCache.put(beanName, factory);
								}
								advisors.addAll(classAdvisors);
							}
		// 省略部分程式碼
		return advisors;
	}

第五步:org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply

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

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

這個方法其實沒啥好分析的,就是根據前面找出來的Advisor集合進行遍歷,然後根據每個Advisor對應的切點來進行匹配,如何合適就返回,對應原始碼也比較簡單,當然前提是你看過我之前那篇AOP原始碼分析的文章了.

總結

這篇文章比較短,因為沒有做很細節的原始碼分析,比較詳細的原始碼分析已經放到上篇文章中了。最後我這裡畫個流程圖總結一下AOP是怎麼被應用到Bean的生命週期中的

image-20200705152704917

Spring原始碼的最後一點補充

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
    // 1.例項化    ---> createBeanInstance
    // 2.屬性注入  ---> populateBean
    // 3.初始化    ---> 完成初始化及AOP
    // exposedObject 就是完成初始化後的Bean  
    // 省略部分程式碼,省略程式碼的作用已經在上面標明瞭
    
    // 下面的程式碼實際上主要目的在於處理迴圈依賴
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            // 我們之前早期暴露出去的Bean跟現在最後要放到容器中的Bean不是同一個
            // allowRawInjectionDespiteWrapping為false
            // 並且當前Bean被當成依賴注入到了別的Bean中
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                // 獲取到當前Bean所從屬的Bean
                String[] dependentBeans = getDependentBeans(beanName);
                // 要得到真實的從屬的Bean
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    // 移除那些僅僅為了型別檢查而建立出來
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if (!actualDependentBeans.isEmpty()) {
					// 丟擲異常
                    // 出現了迴圈依賴,並且實際存在容器中的Bean跟被當作依賴注入到別的Bean中的
                    // 不是同一個物件,這個時候也報錯
                }
            }
        }
    }

    // 註冊bean的銷燬回撥
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

實際這段程式碼還是跟迴圈依賴相關,迴圈依賴是Spring中一個比較重要的話題,不管是為了面試還是更好的瞭解清楚Spring的流程都很有必要去弄懂它

關於Spring的迴圈依賴,我將在下篇文章專門分析!

如果本文對你有幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜尋:程式設計師DMZ,或者掃描下方二維碼,跟著我一起認認真真學Java,踏踏實實做一個coder。

公眾號

我叫DMZ,一個在學習路上匍匐前行的小菜鳥!

相關文章