《系列二》-- 9、bean屬性填充

閱讀之前要注意的東西:本文就是主打流水賬式的源碼閱讀,主導的是一個參考,主要內容需要看官自己去源碼中驗證。全系列文章基於 spring 源碼 5.x 版本。

寫在開始前的話:

閱讀spring 源碼實在是一件龐大的工作,不說全部內容,單就最基本核心部分包含的東西就需要很長時間去消化了:

  • beans
  • core
  • context

實際上我在博客裏貼出來的還只是一部分內容,更多的內容,我放在了個人,fork自 spring 官方源碼倉了; 而且對源碼的學習,必須是要跟着實際代碼層層遞進的,不然只是乾巴巴的文字味同嚼蠟。

https://gitee.com/bokerr/spring-framework-5.0.x-study

這個倉設置的公共倉,可以直接拉取。



Spring源碼閱讀系列--全局目錄.md



一、概述: populateBean 在什麼時候執行?

在getBean 的調用鏈路中,如果是從零開始創建一個bean, 按照潦草的說法大致如下:

  • 1.根據 beanName 從 beanFactory 讀取 BeanDefinition

  • 2.對BeanDefinition 進行簡單的加工、轉化 -> RootBeanDefinition

  • 2.根據bean元數據(BeanDefinition) 中記錄的信息,遞歸的去加載該bean 依賴的其它bean

  • 3.根據bean 的作用域: [prototype、singleton...等等] 決定具體的創建bean的行爲。

    • singleton 需要應用三級緩存,保證全局唯一
    • prototype 每次直接創建全新bean
  • 4.bean的實例化: (所謂實例化,可以理解爲,從java 堆上創建了一個'全新'的對象。)
    根據 BeanDefinition 獲取: bean的類型、參數、自定義構造函數、 注入方式(直接注入: 無參構造函數,間接注入: FactoryBean、factory-bean + factory-method)。

    最終根據上述的信息的配置情況,實例化一個bean。

  • 5.上一步也提到,它只完成了實例化,但是如果有屬性需要填充呢? 參考如下的例子:


<beans>
  <bean id="myBean" class="demo.self.MySimpleBean">
    <!-- property 是常量 -->
    <property name="name" value="Bokerr"/>
    <property name="age" value="27"/>
  </bean>

  <bean id="myBean" class="demo.self.MyBean">
    <!-- property 是一個bean -->
    <property name="bean1"><ref bean="populate1"/></property>
  </bean>
  <bean id="populate1" class="demo.self.populateBean"/>
</beans>

爲什麼說填充的就是 property 屬性呢? 【詳見後文,populateBean方法的第一個判斷。】

bean 創建實例化之後,還處於一個 '空白' 狀態,所以需要經過填充操作,將配置的 property 屬性的值注入。

這就引入了本文需要講述的操作:populateBean

這裏有個很重要、很重要、很重要的起手式:

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

你需要記住: PropertyValues(它是 bean的全部 property 解析後得到的元數據信息)
它是來自於 mbd 的。(也就是 BeanDefinition),它的對象聲明週期受到 BeanFactory 管理;

下文中任意對 PropertyValues 及其屬性的 set 操作,都可等效認爲,實在對 BeanDefinition 作緩存操作。

二、populateBean 的重要操作

/**
 * Populate the bean instance in the given BeanWrapper with the property values
 * from the bean definition.
 *
 * @param beanName the name of the bean
 * @param mbd      the bean definition for the bean
 * @param bw       the BeanWrapper with bean instance
 */
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    if (bw == null) {
        if (mbd.hasPropertyValues()) {
        //  bean 有屬性需要被填充,但是對象包裝器 bw 爲空
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        } else {
            //  包裝器爲空,且不需要 填充任何屬性
            // Skip property population phase for null instance.
            return;
        }
    }

    // 給 InstantiationAwareBeanPostProcessors  最後一次機會,通過屬性注入改變 bean.
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                // 所有 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()的返回值將決定
                // [是/否] 繼續進行property屬性填充
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }
    }

    // 所有需要注入的屬性, 對應的bean緩存 【這裏明確從 BeanDefinition 上獲取的 property 的定義】
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            // {重要操作1-根據Bean名稱注入}   [下文將展開詳說]
            autowireByName(beanName, mbd, bw, newPvs);  //  提取依賴的bean  保存到 pvs
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            // {重要操作1-根據Bean類型注入 <Class>}  [下文將展開詳說]
            autowireByType(beanName, mbd, bw, newPvs);  //  提取依賴的bean  緩存到 pvs
        }
        pvs = newPvs;
    }

    // beanFactory 是否已經註冊了: bean 實例化相關的 "後置處理器"
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // 是否需要依賴檢查
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

    if (hasInstAwareBpps || needsDepCheck) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        // 過濾屬性描述符,爲依賴檢查做準備
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {//  後處理器 ?? 
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // 主要這裏是相同的後置處理器,不同的方法
                    // 字段填充的前置檢查
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {//  依賴檢查, depend-on 屬性,spring-3.0棄用
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

    if (pvs != null) {
        // 屬性填充  {重要操作2} [下文將展開詳說]
        applyPropertyValues(beanName, mbd, bw, pvs);  //  將  propertyValue 應用/注入 到 bean 中
    }
}

這裏重點說兩個操作:

  • 1.屬性值的注入, 將propertyName視作beanName, 並嘗試從spring容器中獲取該bean, 如果成功獲取則通過 PropertyValues:pvs 保存

autowireByName(beanName, mbd, bw, newPvs);
autowireByType(beanName, mbd, bw, newPvs);

  • 2.屬性值的應用, 上述操作的後續只要存在 property 屬性配置就進行 property 值的'應用'操作。

applyPropertyValues(beanName, mbd, bw, pvs);

三、重點操作一 propertyValue 的注入

3.1 根據 Bean名稱注入

    /**
	 * Fill in any missing property values with references to
	 * other beans in this factory if autowire is set to "byName".
	 *
	 * @param beanName the name of the bean we're wiring up.
	 *                 Useful for debugging messages; not used functionally.
	 * @param mbd      bean definition to update through autowiring
	 * @param bw       the BeanWrapper from which we can obtain information about the bean
	 * @param pvs      the PropertyValues to register wired objects with
	 */
	protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

        // 從 BeanDefinition 讀取,非-常量注入的屬性,這裏返回的都是 property 指向別的 bean的場景
        // 這裏會忽略: 基本類型、字符串常量等等 property
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			if (containsBean(propertyName)) {
				// 遞歸加載依賴的bean
				Object bean = getBean(propertyName);
				pvs.add(propertyName, bean);
				// 在容器中記錄 bean 及其依賴的 bean之間的關係, 不妨進去瞅瞅
				registerDependentBean(propertyName, beanName);
				if (logger.isDebugEnabled()) {
					logger.debug("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			} else {
				// 根據beanName 未定位到bean, 記錄到日誌。[說明該 property 所依賴的可能並不是一個 bean,可能只是一個普通的常量。]
				if (logger.isTraceEnabled()) {
					logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
							"' by name: no matching bean found");
				}
			}
		}
	}

上文的內容很簡單:

  • 獲取所有的property中的非 【基本類型、常量】形式的 property
  • 然後遍歷並逐個解析
  • 應用 containsBean 方法,判斷該 propertyName 是否是一個beanName,如果不是則說明property 可能是常量。
  • registerDependentBean(propertyName, beanName); 見名知意,就是記錄當前加載的bean,依賴 propertyName 指向的bean。

3.2 淺看一下,獲取非'簡單' 類型 property 的方法

    /**
	 * Return an array of non-simple bean properties that are unsatisfied.
	 * These are probably unsatisfied references to other beans in the
	 * factory. Does not include simple properties like primitives or Strings.
	 *
	 * @param mbd the merged bean definition the bean was created with
	 * @param bw  the BeanWrapper the bean was created with
	 * @return an array of bean property names
	 * @see org.springframework.beans.BeanUtils#isSimpleProperty
	 */
	protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
		Set<String> result = new TreeSet<>();
		PropertyValues pvs = mbd.getPropertyValues();
		PropertyDescriptor[] pds = bw.getPropertyDescriptors();
		for (PropertyDescriptor pd : pds) {
			// 有 setter方法
			// 		&& 沒有別識別過的依賴
			// 			&& 已經解析過的property字段中不包含該字段
			// 				&& property的類型不是簡單類型(Date、Number、CharSequence、Enum等等)
			if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
					!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
				result.add(pd.getName());
			}
		}
		return StringUtils.toStringArray(result);
	}

3.3 根據 Bean類型注入

    /**
	 * Abstract method defining "autowire by type" (bean properties by type) behavior.
	 * <p>This is like PicoContainer default, in which there must be exactly one bean
	 * of the property type in the bean factory. This makes bean factories simple to
	 * configure for small namespaces, but doesn't work as well as standard Spring
	 * behavior for bigger applications.
	 *
	 * @param beanName the name of the bean to autowire by type
	 * @param mbd      the merged bean definition to update through autowiring
	 * @param bw       the BeanWrapper from which we can obtain information about the bean
	 * @param pvs      the PropertyValues to register wired objects with
	 */
	protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

		TypeConverter converter = getCustomTypeConverter();  //  獲取自定義類型轉換器
		if (converter == null) {
			converter = bw;
		}
		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);  //  緩存依賴關係映射
		// 從 BeanDefinition 讀取,非-常量注入的屬性,這裏返回的都是 property 指向別的 bean的場景
		// 這裏會忽略: 基本類型、字符串常量等等 property
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			try {
				// 我們這是根據 bean Type 注入,所以這裏淺淺的獲取了一下, bean的類對象反射信息
				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
				// Don't try autowiring by type for type Object: never makes sense,
				// even if it technically is a unsatisfied, non-simple property.
				if (Object.class != pd.getPropertyType()) {
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);  //  探測屬性指定的set 方法 
					// Do not allow eager init for type matching in case of a prioritized post-processor.
					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);

					//  依賴描述符
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); 

					// 解析指定 beanName 的屬性所匹配的值,
					// 並把解析到的屬性名稱存儲在 autowiredBeanNames 中
					// 當存在多個屬性被封裝到一起時:
					// @Autowired List<A> aList;  會把 所有 A 類型的bean 都注入其中 autowiredBeans (複數個)
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);  //  尋找類型匹配的  bean  
					if (autowiredArgument != null) {
						pvs.add(propertyName, autowiredArgument);
					}
					for (String autowiredBeanName : autowiredBeanNames) {
						registerDependentBean(autowiredBeanName, beanName);  //  註冊依賴關係
						if (logger.isDebugEnabled()) {
							logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
									propertyName + "' to bean named '" + autowiredBeanName + "'");
						}
					}
					autowiredBeanNames.clear();
				}
			} catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
			}
		}
	}

這裏是根據 property 的類型來注入的,比起根據 propertyName 注入, 這裏的操作複雜得多

  • 獲取所有的property中的非 【基本類型、常量】形式的 property
  • 依次遍歷
  • 結合 BeanDefinition 中的 property 定義,以及包裝器BeanWrapper 中的記錄信息, 得到 property的類型
  • 根據 bean類型描述進入方法: resolveDependency() 及其後文其實內容是相當豐富的,這裏就不再展開了;
    該方式看似沒有返回值,實際上你看下 desc 的構造,你就能發現有什麼東西偷偷摸摸的進去了。

如果想要深究 resolveDependency() 方法,可以下載我個人fork 的spring源碼倉; 我在源碼裏也加了詳細註釋。

https://gitee.com/bokerr/spring-framework-5.0.x-study

四、注入依賴的應用

書接上回 現在我們看第二個重點操作:

  • populateBean().applyPropertyValues()

    /**
	 * Apply the given property values, resolving any runtime references
	 * to other beans in this bean factory. Must use deep copy, so we
	 * don't permanently modify this property.
	 *
	 * @param beanName the bean name passed for better exception information
	 * @param mbd      the merged bean definition
	 * @param bw       the BeanWrapper wrapping the target object
	 * @param pvs      the new property values
	 */
	protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		if (pvs.isEmpty()) {
			return;  //  不需要任何的注入操作
		}

		if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
			// 權限處理
			((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
		}

		MutablePropertyValues mpvs = null;
		List<PropertyValue> original;

		if (pvs instanceof MutablePropertyValues) {
			// 實際上,如果執行過: autowireByType 和 autowireByName 後注入的 <property, propertyValue>
			mpvs = (MutablePropertyValues) pvs;
			if (mpvs.isConverted()) {
				// 根據 name / type 解析到的 bean 已經被 轉化爲 具體類型 那麼直接進行注入操作
				// Shortcut: use the pre-converted values as-is.
				try {
					// 將解析完成的 <property,propertyValue> 逐個應用到包裝器: BeanWrapper
					bw.setPropertyValues(mpvs);
					return;
				} catch (BeansException ex) {
					throw new BeanCreationException(
							mbd.getResourceDescription(), beanName, "Error setting property values", ex);
				}
			}
			original = mpvs.getPropertyValueList();
		} else {
			// 同樣 applyPropertyValues 也可能在別的場景中被調用:
			// 該場景中,PropertyValues 尚未進行任意的 property 的注入操作
			// 所以進入當前分支,繼續往下分析。
			original = Arrays.asList(pvs.getPropertyValues());
		}

		//  獲取自定義-轉換器 其實這個應該並不陌生,在 populateBean().autowireByType() 方法中見過
		// 其實到這裏,可以簡單做個推論了: 接下來的篇幅中,所作的事情,可能會跟 property 的注入操作類似
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;  //  默認使用 bean 包裝器 
		}
		// Spring表達式語言(SpEL) 解析器  #{bean.xxx} ${xx.key}
		// 可由 ApplicationContext 注入
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

		// Create a deep copy, resolving any references for values.
		List<PropertyValue> deepCopy = new ArrayList<>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			//  遍歷 property 屬性,將需要轉換的屬性,轉換爲對應的類型
			if (pv.isConverted()) {
				// 已經轉化過,直接添加 [BeanDefinition 緩存應用 或者 前邊的環節已經處理過]
				deepCopy.add(pv);
			} else {
				// 先轉換 後添加
				String propertyName = pv.getName();
				// 原始值
				Object originalValue = pv.getValue();
				// 包含SpEL表達式的解析
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;

				// property 是否可覆蓋?
				// 根據包裝器 bw 判斷 property 字段可寫 && 並且 property 不是(索引/嵌套類型的屬性)。
				// 自引入概念:property 應該是一個 "原子屬性"
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					// 可以寫入,且是一個"原子屬性", 那麼就可以進行轉化了
					// 這裏不太適合再鑽進去了,除非spring源碼出bug了,否則如下的邏輯是不太有可能接觸到的,
					// 可以簡單做個蓋棺定論: 這裏會把 resolvedValue 轉化爲 property 字段所期望的值類型:
					// 		String: 2023-07-10   ->  Date: 2023-07-10 00:00:00.000
					// 		String: false        ->  Boolean: false
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}

				// Possibly store converted value in merged bean definition,
				// in order to avoid re-conversion for every created bean instance.
				if (resolvedValue == originalValue) {
					// 轉換後的值(對象)  等價  原始值
					if (convertible) {
						// PropertyValue 是元數據 BeanDefinition 的一環,這裏等價爲緩存
						// 避免重複的轉換操作
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				} else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					// 轉化後的值不等於原始值
					// 		&& 支持轉換
					// 		&& 原始值是String
					// 		&& 原始值不是動態的(常量)
					// 		&&  轉換後的值,不是集合、不是數組
					pv.setConvertedValue(convertedValue);  // BeanDefinition 設置緩存
					deepCopy.add(pv);
				} else {
					// 上述情形之外的情況, 可以看到,區別是,這裏沒有被當作緩存設回 BeanDefinition 中
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			// mpvs 同樣是 BeanDefinition 所包含的信息,這裏也可視作緩存設置
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		try {
			// 將解析完成的 <property,propertyValue> 逐個應用到包裝器: BeanWrapper
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		} catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}
	}

重要信息都放在註釋裏了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章