主頁 > 後端開發 > Spring Ioc原始碼分析系列--Bean實體化程序(二)

Spring Ioc原始碼分析系列--Bean實體化程序(二)

2022-06-01 21:38:20 後端開發

Spring Ioc原始碼分析系列--Bean實體化程序(二)

前言

上篇文章Spring Ioc原始碼分析系列--Bean實體化程序(一)簡單分析了getBean()方法,還記得分析了什么嗎?不記得了才是正常的,記住了才是怪人,忘記了可以回去翻翻,翻不翻都沒事, 反正最后都會忘了,

這篇文章是給上篇填坑的,上篇分析到真正創建Bean的createBean(beanName, mbd, args)就沒有繼續深入去分析了,繞得太深,說不清楚,那么這一篇,就續上這個口子,去分析createBean(beanName, mbd, args)方法,

原始碼分析

話不多說,我們直接來到createBean(beanName, mbd, args)方法的原始碼,具體的實作是在AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)里,可以直接定位到這里,

createBean()方法

跟進代碼查看,這個方法也比較簡單,主要分為了以下幾點:

  • 初始化化Class物件,呼叫resolveBeanClass(mbd, beanName)方法獲取class物件,這里會去決議類全限定名,最終是通過反射方法Class<?> resolvedClass = ClassUtils.forName(className, classLoader)獲取Class物件,
  • 檢查覆寫方法,對應的是mbdToUse.prepareMethodOverrides()方法,這里會對一些多載方法進行標記預處理,如果同方法名的方法只存在一個,那么會將覆寫標記為未多載,以避免 arg 型別檢查的開銷,
  • 應用后置處理器,在實體化物件前,會經過后置處理器處理,這個后置處理器的提供了一個短路機制,就是可以提前結束整個Bean的生命周期,直接從這里回傳一個Bean,
  • 創建Bean,呼叫doCreateBean()方法進行Bean的創建,在Spring里面,帶有do開頭的一般是真正干活的方法,所以Ioc創建Bean到這里,才是真正要到干活的地方了,

我們庖丁解牛先把方法不同的功能按照邏輯拆分了,那接下來,就詳細分析一下每個部分,

	/**
	 * Central method of this class: creates a bean instance,
	 * populates the bean instance, applies post-processors, etc.
	 *
	 * 此類的中心方法:創建 bean 實體、填充 bean 實體、應用后處理器等,
	 *
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		//鎖定class ,根據設定的 class 屬性或者根據 className 來決議 Class
		// 決議得到beanClass,為什么需要決議呢?如果是從XML中決議出來的標簽屬性肯定是個字串嘛
		// 所以這里需要加載類,得到Class物件
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.驗證及準備覆寫的方法
		// 對XML標簽中定義的lookUp屬性進行預處理,
		// 如果只能根據名字找到一個就標記為非多載的,這樣在后續就不需要去推斷到底是哪個方法了,
		// 對于@LookUp注解標注的方法是不需要在這里處理的,
		// AutowiredAnnotationBeanPostProcessor會處理這個注解
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			//給BeanPostProcessors一個露臉的機會
			// 在實體化物件前,會經過后置處理器處理
			// 這個后置處理器的提供了一個短路機制,就是可以提前結束整個Bean的生命周期,直接從這里回傳一個Bean
			// 不過我們一般不會這么做,它的另外一個作用就是對AOP提供了支持,
			// 在這里會將一些不需要被代理的Bean進行標記,就本IoC系列文章而言,你可以暫時理解它沒有起到任何作用
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			//若果有自定義bean則直接回傳了bean,不會再走后續的doCreateBean方法
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			// 不存在提前初始化的操作,開始正常的創建流程
			// doXXX方法,真正干活的方法,doCreateBean,真正創建Bean的方法
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// 省略部分例外..
		}
		}
	}

初始化Class物件

很顯然初始化Class物件的代碼在resolveBeanClass(mbd, beanName)方法里,跟進代碼查看,

	/**
	 * Resolve the bean class for the specified bean definition,
	 * resolving a bean class name into a Class reference (if necessary)
	 * and storing the resolved Class in the bean definition for further use.
	 *
	 * 為指定的 bean 定義決議 bean 類,將 bean 類名稱決議為 Class 參考(如果需要)并將決議的 Class 存盤在 bean 定義中以供進一步使用,
	 *
	 * @param mbd the merged bean definition to determine the class for
	 * @param beanName the name of the bean (for error handling purposes)
	 * @param typesToMatch the types to match in case of internal type matching purposes
	 * (also signals that the returned {@code Class} will never be exposed to application code)
	 *       在內部型別匹配的情況下要匹配的型別(也表示回傳的 {@code Class} 永遠不會暴露給應用程式代碼)
	 * @return the resolved bean class (or {@code null} if none)
	 * @throws CannotLoadBeanClassException if we failed to load the class
	 */
	@Nullable
	protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
			throws CannotLoadBeanClassException {

		try {
			// 如果已經創建過,直接回傳
			if (mbd.hasBeanClass()) {
				return mbd.getBeanClass();
			}
			if (System.getSecurityManager() != null) {
				return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
					doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
			}
			else {
				// 否則進行創建
				return doResolveBeanClass(mbd, typesToMatch);
			}
		}
		catch (PrivilegedActionException pae) {
			// 省略部分例外處理
		}
	}

跟進doResolveBeanClass(mbd, typesToMatch)方法,我們這里傳入的typesToMatch引數物件陣列為空,所以不會走排除部分類的邏輯,接下來是使用evaluateBeanDefinitionString()方法計算運算式如果傳入的className有占位符,會在這里被決議,最終正常我們會走到mbd.resolveBeanClass(beanClassLoader)方法里,

	private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
			throws ClassNotFoundException {

		// 獲取類加載器
		ClassLoader beanClassLoader = getBeanClassLoader();
		ClassLoader dynamicLoader = beanClassLoader;
		boolean freshResolve = false;

		if (!ObjectUtils.isEmpty(typesToMatch)) {
			// When just doing type checks (i.e. not creating an actual instance yet),
			// use the specified temporary class loader (e.g. in a weaving scenario).
			// 當只是進行型別檢查(即尚未創建實際實體)時,請使用指定的臨時類加載器(例如在編織場景中),
			ClassLoader tempClassLoader = getTempClassLoader();
			if (tempClassLoader != null) {
				dynamicLoader = tempClassLoader;
				freshResolve = true;
				if (tempClassLoader instanceof DecoratingClassLoader) {
					DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
					for (Class<?> typeToMatch : typesToMatch) {
						dcl.excludeClass(typeToMatch.getName());
					}
				}
			}
		}

		String className = mbd.getBeanClassName();
		if (className != null) {
			Object evaluated = evaluateBeanDefinitionString(className, mbd);
			if (!className.equals(evaluated)) {
				// A dynamically resolved expression, supported as of 4.2...
				if (evaluated instanceof Class) {
					return (Class<?>) evaluated;
				}
				else if (evaluated instanceof String) {
					className = (String) evaluated;
					freshResolve = true;
				}
				else {
					throw new IllegalStateException("Invalid class name expression result: " + evaluated);
				}
			}
			if (freshResolve) {
				// When resolving against a temporary class loader, exit early in order
				// to avoid storing the resolved Class in the bean definition.
				// 當針對臨時類加載器決議時,請提前退出以避免將決議的類存盤在 bean 定義中,
				if (dynamicLoader != null) {
					try {
						// 使用臨時動態加載器加載 class 物件
						return dynamicLoader.loadClass(className);
					}
					catch (ClassNotFoundException ex) {
						if (logger.isTraceEnabled()) {
							logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
						}
					}
				}
				// 反射加載 class 物件
				return ClassUtils.forName(className, dynamicLoader);
			}
		}

		// Resolve regularly, caching the result in the BeanDefinition...
		// 正常決議,將結果快取在 BeanDefinition...
		return mbd.resolveBeanClass(beanClassLoader);
	}

跟進mbd.resolveBeanClass(beanClassLoader)方法,可以看到這里就是使用反射初始化Class物件,然后快取在BeanDefinition中,到這里,已經完成了從一個字串的類名到一個Class物件的轉換了,我們已經得到了一個可以使用的Class物件,

	/**
	 * Determine the class of the wrapped bean, resolving it from a
	 * specified class name if necessary. Will also reload a specified
	 * Class from its name when called with the bean class already resolved.
	 *
	 * 確定被包裝的 bean 的類,必要時從指定的類名決議它,當使用已決議的 bean 類呼叫時,還將從其名稱中重新加載指定的類,
	 *
	 * @param classLoader the ClassLoader to use for resolving a (potential) class name
	 * @return the resolved bean class
	 * @throws ClassNotFoundException if the class name could be resolved
	 */
	@Nullable
	public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {
		String className = getBeanClassName();
		if (className == null) {
			return null;
		}
		Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
		this.beanClass = resolvedClass;
		return resolvedClass;
	}

檢查覆寫方法

初始化class物件已經完成了,接下來會去處理多載方法,處理的邏輯在mbdToUse.prepareMethodOverrides()方法里,

摘取《Spring原始碼深度決議》里面的一段話:

很多讀者可能會不知道這個方法的作用,因為在 Spring 的配置里面根本就沒有諸如 override-method 之類的配置, 那么這個方法到底是干什么用的呢? 其實在 Spring 中確實沒有 override-method 這樣的配置,但是在 Spring 配置中是存在 lookup-methodreplace-method 的,而這兩個配置的加載其實就是將配置統一存放在 BeanDefinition 中的 methodOverrides 屬性里,而這個函式的操作其實也就是針對于這兩個配置的,

lookup-method通常稱為獲取器注入,spring in action 中對它的描述是,一種特殊的方法注入,它是把一個方法宣告為回傳某種型別的 bean,而實際要回傳的 bean 是在組態檔里面配置的,可用在設計可插拔的功能上,解除程式依賴, 這里會對一些多載方法進行標記預處理,如果同方法名的方法只存在一個,那么會將覆寫標記為未多載,以避免 arg 型別檢查的開銷,

這種騷操作我們基本是不會使用的,所以簡單看一下代碼,淺嘗輒止,有興趣可以去翻翻,

	/**
	 * Validate and prepare the method overrides defined for this bean.
	 * Checks for existence of a method with the specified name.
	 *
	 * 驗證并準備為此 bean 定義的方法覆寫,檢查具有指定名稱的方法是否存在,
	 *
	 * @throws BeanDefinitionValidationException in case of validation failure
	 */
	public void prepareMethodOverrides() throws BeanDefinitionValidationException {
		// Check that lookup methods exist and determine their overloaded status.
		// 檢查查找方法是否存在并確定它們的多載狀態,
		if (hasMethodOverrides()) {
			getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
		}
	}

可以看到這里就獲取所有methodOverrides,然后遍歷去呼叫prepareMethodOverride()方法,跟進prepareMethodOverride()方法,可以看到這里就是做個簡單的標記,

	/**
	 * Validate and prepare the given method override.
	 * Checks for existence of a method with the specified name,
	 * marking it as not overloaded if none found.
	 *
	 * 驗證并準備給定的方法覆寫,檢查具有指定名稱的方法是否存在,如果沒有找到,則將其標記為未多載,
	 *
	 * @param mo the MethodOverride object to validate
	 * @throws BeanDefinitionValidationException in case of validation failure
	 */
	protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
		int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
		if (count == 0) {
			throw new BeanDefinitionValidationException(
					"Invalid method override: no method with name '" + mo.getMethodName() +
					"' on class [" + getBeanClassName() + "]");
		}
		else if (count == 1) {
			// Mark override as not overloaded, to avoid the overhead of arg type checking.
			// 將覆寫標記為未多載,以避免 arg 型別檢查的開銷,
			mo.setOverloaded(false);
		}
	}

應用后置處理器

在實體化物件前,會經過后置處理器處理,這個后置處理器的提供了一個短路機制,就是可以提前結束整個Bean的生命周期,直接從這里回傳一個Bean,不過我們一般不會這么做,它的另外一個作用就是對AOP提供了支持,在這里會將一些不需要被代理的Bean進行標記,就本IoC系列文章而言,你可以暫時理解它沒有起到任何作用,

跟進代碼resolveBeforeInstantiation(beanName, mbdToUse)查看,

	/**
	 * Apply before-instantiation post-processors, resolving whether there is a
	 * before-instantiation shortcut for the specified bean.
	 *
	 * 應用實體化前后處理器,決議指定 bean 是否存在實體化前快捷方式,
	 * 實體化前的快捷方式的意思這里可能會直接回傳一個定義的代理,而不需要在把目標類初始化
	 *
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @return the shortcut-determined bean instance, or {@code null} if none
	 */
	//注意單詞Instantiation和Initialization區別
	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				// 確定給定的 bean 的型別
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					// 提供一個提前初始化的時機,這里會直接回傳一個實體物件
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						// 如果提前初始化成功,則執行 postProcessAfterInitialization() 方法,注意單詞Instantiation和Initialization區別
						// 關于這一塊的邏輯,細心的一點的會發現,這里漏了實體化后置處理、初始化前置處理這兩個方法,
						// 而是在提前回傳物件后,直接執行了初始化后置處理器就完成了bean的整個流程,
						// 相當于是提供了一個短路的操作,不再經過Spring提供的繁雜的各種處理
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			// 設定是否已經提前實體化
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

跟進applyBeanPostProcessorsBeforeInstantiation()代碼查看,這里只要有一個 InstantiationAwareBeanPostProcessor 回傳的結果不為空,則直接回傳,說明多個 InstantiationAwareBeanPostProcessor 只會生效靠前的一個,注意單詞Instantiation和Initialization區別

	/**
	 * spring bean 初始化流程
	 * Bean 初始化(Initialization)
	 * 1.@PoseConstruct 方法
	 * 2.實作InitializingBean 介面的afterPropertiesSet()方法
	 * 3.自定義初始化方法
	 */
	@Nullable
	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);
				// 如果為空,表示不作任何調整
				// 這里只要有一個 InstantiationAwareBeanPostProcessor 回傳的結果不為空,則直接回傳,
				// 說明多個 InstantiationAwareBeanPostProcessor 只會生效靠前的一個
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

跟進applyBeanPostProcessorsAfterInitialization()方法,邏輯跟上面的是類似的,注意單詞Instantiation和Initialization區別

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

創建Bean

經過上面的步驟,有驚無險,我們來到了doCreateBean(beanName, mbdToUse, args)方法,這是真正進行Bean創建的地方,所以這里才是真的進入正文,前面都是打醬油走走程序,

當經歷過 resolveBeforelnstantiation() 方法后,程式有兩個選擇 ,如果創建了代理或者說重寫了 InstantiationAwareBeanPostProcessorpostProcessBeforelnstantiation() 方法并在方法 postProcessBeforelnstantiation() 中改變了 bean, 則直接回傳就可以了 , 否則需要進行常規 bean 的創建, 而這常規 bean 的創建就是在 doCreateBean() 中完成的,

直接跟進doCreateBean()代碼查看,代碼很長,你忍一下,

	/**
	 * Actually create the specified bean. Pre-creation processing has already happened
	 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
	 * <p>Differentiates between default bean instantiation, use of a
	 * factory method, and autowiring a constructor.
	 *
	 * 實際創建指定的bean,
	 * 此時已經進行了預創建處理,例如檢查 {@code postProcessBeforeInstantiation} 回呼,
	 * <p>區分默認 bean 實體化、使用工廠方法和自動裝配建構式,
	 *
	 * @param beanName the name of the bean
	 * @param mbd the merged bean definition for the bean
	 * @param args explicit arguments to use for constructor or factory method invocation
	 * @return a new instance of the bean
	 * @throws BeanCreationException if the bean could not be created
	 * @see #instantiateBean
	 * @see #instantiateUsingFactoryMethod
	 * @see #autowireConstructor
	 */
	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
		// 這個方法真正創建了Bean,創建一個Bean會經過 創建物件 > 依賴注入 > 初始化
		// 這三個程序,在這個程序中,BeanPostProcessor會穿插執行,

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			//根據指定bean使用對應的策略創建新的實體,如工廠方法,建構式自動注入,簡單初始化
			// 這里真正的創建了物件
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.

		// 按照官方的注釋來說,這個地方是Spring提供的一個擴展點,
		// 對程式員而言,我們可以通過一個實作了MergedBeanDefinitionPostProcessor的后置處理器
		// 來修改bd中的屬性,從而影響到后續的Bean的生命周期
		// 不過官方自己實作的后置處理器并沒有去修改bd,
		// 而是呼叫了applyMergedBeanDefinitionPostProcessors方法
		// 這個方法名直譯過來就是-應用合并后的bd,也就是說它這里只是對bd做了進一步的使用而沒有真正的修改
		synchronized (mbd.postProcessingLock) {
			// bd只允許被處理一次
			if (!mbd.postProcessed) {
				try {
					// 應用合并后的bd
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				// 標注這個bd已經被MergedBeanDefinitionPostProcessor的后置處理器處理過
				// 那么在第二次創建Bean的時候,不會再次呼叫applyMergedBeanDefinitionPostProcessors
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//是否需要提前暴露mbd.isSingleton() && this.allowCircularReferences &&
		//				isSingletonCurrentlyInCreation(beanName)
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//為避免后期回圈依賴,可以在bean初始化完成前將創建實體的ObjectFactory加入工廠
			//getEarlyBeanReference對bean再一次依賴參考,主要應用SmartInstantiationAwareBeanPostProcessor
			//其中我們熟悉的AOP就是在這里將advice動態織入bean中,若沒有則直接回傳bean,不做任何處理
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		// 初始化實體
		Object exposedObject = bean;
		try {
			//對bean進行填充,對各個屬性進行注入,可能存在依賴其他bean的屬性,則會遞回初始化依賴bean
			populateBean(beanName, mbd, instanceWrapper);
			//呼叫初始化方法
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			//earlySingletonReference只有在檢測到回圈依賴的情況下才不為空
			if (earlySingletonReference != null) {
				//如果exposedObject沒有在初始化方法中被改變,也就是沒有被增強
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					//檢測依賴
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					/**
					 * 因為bean創建完成后,其依賴的bean也一定是創建完成的
					 * 如果actualDependentBeans不為空,則說明依賴的bean還沒有被完全創建好
					 * 也就是說還存在回圈依賴
					 */
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			//根據scope注冊bean
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

分析一下這個函式設計思路:

  • 如果是單例則需要首先清除快取,
  • 實體化 bean ,將 BeanDefinition 轉換為 BeanWrapper, 轉換是一個復雜的程序,但是我們可以嘗試概括大致的功能,如下所示,
    • 如果存在工廠方法則使用工廠方法進行實體化,
    • 如果一個類有多個建構式,每個建構式都有不同的引數,所以需要根據引數鎖定構造 函式并進行實體化,
    • 如果既不存在工廠方法也不存在帶有引數的建構式,則使用默認的建構式進行 bean 的實體化,
  • MergedBeanDefinitionPostProcessor的應用, bean 合并后的處理, Autowired 注解正是通過此方法實作諸如型別的預決議,
  • 依賴處理, 在 Spring 中會有回圈依賴的情況,例如,當 A 中含有 B 的屬性,而 B 中又含有 A 的屬性 時就會構成一個回圈依賴,此時如果 A 和 B 都是單例,那么在 Spring 中的處理方式就是當創建 B 的時候,涉及自動注入 A 的步驟,并不是直接去再次創建 A,而是通過放入快取中的 ObjectFactory 來創建實體,這樣就解決了回圈依賴的問題,
  • 屬性填充, 將所有屬性填充至 bean 的實體中,
  • 呼叫初始化方法,在屬性填充完成后,這里會進行初始化方法的呼叫,
  • 回圈依賴檢查, 之前有提到過,在 Sping 中解決回圈依賴只對單例有效,而對于 prototype 的 bean, Spring 沒有好的解決辦法,唯一要做的就是拋出例外, 在這個步驟里面會檢測已經加載的 bean 是否 已經出現了依賴回圈,并判斷是再需要拋出例外,
  • 注冊 DisposableBean, 如果配置了 destroy-method,這里需要注冊以便于在銷毀時候呼叫,
  • 完成創建井回傳,

可以看到上面的步驟非常的繁瑣,每一步驟都使用了大量的代碼來完成其功能,最復雜也是最難以理解的當屬回圈依賴的處理,在真正進入 doCreateBean() 前我們有必要先了解下回圈依賴,這里會在下一篇文章Spring Ioc原始碼分析系列--自動注入回圈依賴的處理圖文并茂去分析,

下面就按照上述的點逐個分析,接下來肯定是枯燥無味的,那開始吧,

清除factoryBeanInstanceCache快取

首先如果是單例,會到factoryBeanInstanceCache中獲取是否存在快取,如果有這里就會從快取里獲取一個instanceWrapper,不需要再去走復雜的創建流程了,

對應代碼如下:

		if (mbd.isSingleton()) {
			// 你可以暫時理解為,這個地方回傳的就是個null
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
實體化 bean

又到了實體化bean,是不是反反復復看了很多次,到底哪里才真的創建一個bean,別慌,這里真的是真正創建bean的地方了,再套娃就是狗,

跟進createBeanInstance(beanName, mbd, args)方法,這個方法干了哪幾件事?

  • 首先嘗試呼叫obtainFromSupplier()實體化bean
  • 嘗試呼叫instantiateUsingFactoryMethod()實體化bean
  • 根據給定引數推斷建構式實體化bean
  • 以上均無,則使用默認建構式實體化bean
	/**
	 * Create a new instance for the specified bean, using an appropriate instantiation strategy:
	 * factory method, constructor autowiring, or simple instantiation.
	 *
	 * 使用適當的實體化策略為指定的 bean 創建一個新實體:工廠方法、建構式自動裝配或簡單實體化,
	 *
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @param args explicit arguments to use for constructor or factory method invocation
	 * @return a BeanWrapper for the new instance
	 * @see #obtainFromSupplier
	 * @see #instantiateUsingFactoryMethod
	 * @see #autowireConstructor
	 * @see #instantiateBean
	 */
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// 確保此時實際決議了 bean 類,
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
		// 通過bd中提供的instanceSupplier來獲取一個物件
		// 正常bd中都不會有這個instanceSupplier屬性,這里也是Spring提供的一個擴展點,但實際上不常用
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		//如果工廠方法不為null,則使用工廠方法初始化策略
		// bd中提供了factoryMethodName屬性,那么要使用工廠方法的方式來創建物件,
		// 工廠方法又會區分靜態工廠方法跟實體工廠方法
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		// 在原型模式下,如果已經創建過一次這個Bean了,那么就不需要再次推斷建構式了
		// 是否推斷過建構式
		boolean resolved = false;
		// 建構式是否需要進行注入
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				//一個類里面有多個建構式,每個建構式都有不同的引數,所以呼叫前需要根據引數鎖定要呼叫的建構式或工廠方法
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		//如果已經決議過則使用決議好的建構式方法,不需要再次鎖定
		if (resolved) {
			if (autowireNecessary) {
				//建構式自動注入
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				//使用默認建構式進行構造
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
		//需要根據引數決議建構式
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			//建構式自動注入
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		// 默認構造的首選建構式?
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
		//使用默認建構式
		return instantiateBean(beanName, mbd);
	}

接下來分析以上幾點,算了不分析,太長了,我在另一篇文章Spring Ioc原始碼分析系列--實體化Bean的幾種方法會填坑,這里會詳細分析上面的幾點,好好把握,估計看到這都不知道啥跟啥了,

MergedBeanDefinitionPostProcessor的應用

到這里我們已經實體化了一個bean物件,但是這個bean只是個半成品,空有外殼而無內在,所以接下來的作業就是對里面的內容進行填充,那毫無疑問,按照Spring的尿性,肯定會在真正開始之前給你一個擴展點,讓你還要機會在屬性填充之前修改某些東西,我們經常使用的@Autowired注解就是在這里實作的,后續會寫一篇Spring Ioc原始碼分析系列--@Autowired注解的實作原理結合原始碼和例子去分析它的實作,

跟進代碼查看,比較簡單,就是獲取所有的MergedBeanDefinitionPostProcessor,然后依次執行它的postProcessMergedBeanDefinition()方法,

	/**
	 * Apply MergedBeanDefinitionPostProcessors to the specified bean definition,
	 * invoking their {@code postProcessMergedBeanDefinition} methods.
	 *
	 * 將 MergedBeanDefinitionPostProcessors 應用于指定的 bean 定義,
	 * 呼叫它們的 {@code postProcessMergedBeanDefinition} 方法,
	 *
	 * 可以看到這個方法的代碼還是很簡單的,
	 * 就是呼叫了MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
	 *
	 * @param mbd the merged bean definition for the bean
	 * @param beanType the actual type of the managed bean instance
	 * @param beanName the name of the bean
	 * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
	 */
	protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof MergedBeanDefinitionPostProcessor) {
				MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
				bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
			}
		}
	}
依賴處理

這部分主要是為了處理回圈依賴而做的準備,這里會根據earlySingletonExposure引數去判斷是否允許回圈依賴,如果允許,則會呼叫addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))方法將bean的早期參考放入到singletonFactories中,關于回圈依賴的詳細處理程序,可以在下一篇文章Spring Ioc原始碼分析系列--自動注入回圈依賴的處理里看到,

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//是否需要提前暴露mbd.isSingleton() && this.allowCircularReferences &&
		//				isSingletonCurrentlyInCreation(beanName)
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//為避免后期回圈依賴,可以在bean初始化完成前將創建實體的ObjectFactory加入工廠
			//getEarlyBeanReference對bean再一次依賴參考,主要應用SmartInstantiationAwareBeanPostProcessor
			//其中我們熟悉的AOP就是在這里將advice動態織入bean中,若沒有則直接回傳bean,不做任何處理
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

跟進addSingletonFactory()方法,可以看到這里會先把早期參考放入到singletonFactories三級快取中,

	/**
	 * Add the given singleton factory for building the specified singleton
	 * if necessary.
	 *
	 * 如有必要,添加給定的單例工廠以構建指定的單例,
	 *
	 * <p>To be called for eager registration of singletons, e.g. to be able to
	 * resolve circular references.
	 *
	 * 被提前注冊的單例Bean呼叫,例如用來解決回圈依賴
	 *
	 * @param beanName the name of the bean
	 * @param singletonFactory the factory for the singleton object
	 */
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

那放入到singletonFactories里面的是什么呢?從上面可以看到,這是一個lambada運算式,呼叫的方法的是getEarlyBeanReference(),跟進代碼查看,

	/**
	 * Obtain a reference for early access to the specified bean,
	 * typically for the purpose of resolving a circular reference.
	 *
	 * 獲取對指定 bean 的早期訪問的參考,通常用于決議回圈參考,
	 *
	 * @param beanName the name of the bean (for error handling purposes)
	 * @param mbd the merged bean definition for the bean
	 * @param bean the raw bean instance
	 * @return the object to expose as bean reference
	 */
	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;
					// 對 bean 再一次依賴參考
					// 主要應用 SmartInstantiationAwareBeanPostProcessor, 
					// 其中我們熟知的 AOP 就是在這里將 advice 動態織入 bean 中, 若沒有則直接回傳 bean ,不做任何處理
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
屬性填充

到這里會已經完成了bean的實體化,早期參考的暴露,那接下來就到了屬性填充的部分,開始對bean進行各種賦值,讓一個空殼半成品bean完善成一個有血有肉的正常bean,

這里可能存在依賴其他bean的屬性,則會遞回初始化依賴bean,

populateBean() 函式中提供了這樣的處理流程,

  • InstantiationAwareBeanPostProcessor 處理器的 postProcessAfterinstantiation 函式的應用, 此函式可以控制程式是否繼續進行屬性填充,
  • 根據注入型別( byName/byType ),提取依賴的 bean,并統一存入 PropertyValues 中,
  • 應用 InstantiationAwareBeanPostProcessor 處理器的 postProcessPropertyValues 方法, 對屬性獲取完畢填充前對屬性的再次處理,典型應用是 RequiredAnnotationBeanPostProcessor 類中對屬性的驗證,
  • 將所有 PropertyValues 中的屬性填充至 BeanWrapper 中,

跟進代碼查看,又很長,這一塊的代碼真的是又臭又長,但是注釋很詳細,可以跟著看看,

	/**
	 * Populate the bean instance in the given BeanWrapper with the property values
	 * from the bean definition.
	 *
	 * 使用 bean 定義中的屬性值填充給定 BeanWrapper 中的 bean 實體,
	 *
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @param bw the BeanWrapper with bean instance
	 */
	@SuppressWarnings("deprecation")  // for postProcessPropertyValues
	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				//沒有可填充的屬性
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		boolean continueWithPropertyPopulation = true;

		//給InstantiationAwareBeanPostProcessors最后一次機會在屬性設定前來改變 bean
		//如:可以用來支持屬性注入的型別
		// 滿足兩個條件,不是合成類 && 存在InstantiationAwareBeanPostProcessor
		// 其中InstantiationAwareBeanPostProcessor主要作用就是作為Bean的實體化前后的鉤子
		// 外加完成屬性注入,對于三個方法就是
		// postProcessBeforeInstantiation  創建物件前呼叫
		// postProcessAfterInstantiation   物件創建完成,@AutoWired注解決議后呼叫
		// postProcessPropertyValues(已過期,被postProcessProperties替代) 進行屬性注入
		// 下面這段代碼的主要作用就是我們可以提供一個InstantiationAwareBeanPostProcessor
		// 提供的這個后置處理如果實作了postProcessAfterInstantiation方法并且回傳false
		// 那么可以跳過Spring默認的屬性注入,但是這也意味著我們要自己去實作屬性注入的邏輯
		// 所以一般情況下,我們也不會這么去擴展
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					//回傳值為是否繼續填充bean
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}

		//如果后處理器發出停止填充命令則終止后續操作
		if (!continueWithPropertyPopulation) {
			return;
		}

		// 這里其實就是判斷XML是否提供了屬性相關配置
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		// 確認注入模型
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();

		// 主要處理byName跟byType兩種注入模型,byConstructor這種注入模型在創建物件的時候已經處理過了
		// 這里都是對自動注入進行處理,byName跟byType兩種注入模型均是依賴setter方法
		// byName,根據setter方法的名字來查找對應的依賴,例如setA,那么就是去容器中查找名字為a的Bean
		// byType,根據setter方法的引數型別來查找對應的依賴,例如setXx(A a),就是去容器中查詢型別為A的bean
		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) {
				//根據名稱注入
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				//根據型別注入
				autowireByType(beanName, mbd, bw, newPvs);
			}
			// pvs是XML定義的屬性
			// 自動注入后,bean實際用到的屬性就應該要替換成自動注入后的屬性
			pvs = newPvs;
		}

		//后置處理器已經初始化
		// 檢查是否有InstantiationAwareBeanPostProcessor
		// 前面說過了,這個后置處理器就是來完成屬性注入的
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		//需要依賴檢查
		//  是否需要依賴檢查,默認是不會進行依賴檢查的
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		// 下面這段代碼有點麻煩了,因為涉及到版本問題
		// 其核心代碼就是呼叫了postProcessProperties完成了屬性注入
		PropertyDescriptor[] filteredPds = null;
		// 存在InstantiationAwareBeanPostProcessor,我們需要呼叫這類后置處理器的方法進行注入
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					// 這句就是核心
					// Autowired 是通過 AutowiredAnnotationBeanPostProcessor#postProcessProperties() 實作的
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						if (filteredPds == null) {
							// 得到需要進行依賴檢查的屬性的集合
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						//對所有需要依賴檢查的屬性做后置處理
						//  這個方法已經過時了,放到這里就是為了兼容老版本
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return;
						}
					}
					pvs = pvsToUse;
				}
			}
		}
		// 需要進行依賴檢查
		if (needsDepCheck) {
			if (filteredPds == null) {
				// 得到需要進行依賴檢查的屬性的集合
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			//依賴檢查,對應depends-on屬性,3.0已經棄用此屬性
			// 對需要進行依賴檢查的屬性進行依賴檢查
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}

		// 將XML中的配置屬性應用到Bean上
		if (pvs != null) {
			//將屬性應用到bean中
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

這里看到這里會根據byName或者byType方式尋找依賴,然后呼叫applyPropertyValues()將屬性注入到BeanWrapperImpl里,

先來看autowireByName()方法,顧名思義,這里會根據屬性名去獲取依賴,

	/**
	 * Fill in any missing property values with references to
	 * other beans in this factory if autowire is set to "byName".
	 * 
	 * 如果 autowire 設定為“byName”,則使用對該工廠中其他 bean 的參考填充任何缺少的屬性值,
	 * 
	 * @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) {

		//尋找bw中需要依賴注入的屬性值
		// 得到符合下面條件的屬性名稱
		// 1.有setter方法
		// 2.需要進行依賴檢查
		// 3.不包含在XML配置中
		// 4.不是簡單型別(基本資料型別,列舉,日期等)
		// 這里可以看到XML配置優先級高于自動注入的優先級
		// 不進行依賴檢查的屬性,也不會進行屬性注入
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			if (containsBean(propertyName)) {
				//遞回初始化相關bean
				Object bean = getBean(propertyName);
				// 將自動注入的屬性添加到pvs中去
				pvs.add(propertyName, bean);
				//注冊依賴
				registerDependentBean(propertyName, beanName);
				if (logger.isTraceEnabled()) {
					logger.trace("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
							"' by name: no matching bean found");
				}
			}
		}
	}

接下來看autowireByType(),該方法會根據屬性的型別去獲取依賴,也比較簡單明了,

	/**
	 * 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.
	 * 
	 * 定義“按型別自動裝配”(按型別的 bean 屬性)行為的抽象方法, 
	 * <p>這類似于 PicoContainer 默認值,其中 bean 工廠中必須只有一個屬性型別的 bean,
	 * 這使得 bean 工廠易于為小型命名空間配置,但不能像標準 Spring 行為那樣為大型應用程式作業,
	 * 
	 * @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) {

		// 這個型別轉換器,主要是在處理@Value時需要使用
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}

		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
		//尋找bw中需要依賴注入的屬性
		// 得到符合下面條件的屬性名稱
		// 1.有setter方法
		// 2.需要進行依賴檢查
		// 3.不包含在XML配置中
		// 4.不是簡單型別(基本資料型別,列舉,日期等)
		// 這里可以看到XML配置優先級高于自動注入的優先級
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			try {
				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.
				// 不要嘗試為 Object 型別按型別自動裝配:永遠沒有意義,即使它在技術上是一個不令人滿意的、不簡單的屬性,
				if (Object.class != pd.getPropertyType()) {
					//探測指定屬性的set方法
					// 這里獲取到的就是setter方法的引數,因為我們需要按照型別進行注入嘛
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
					// Do not allow eager init for type matching in case of a prioritized post-processor.
					// 如果是PriorityOrdered在進行型別匹配時不會去匹配factoryBean
					// 如果不是PriorityOrdered,那么在查找對應型別的依賴的時候會會去匹factoryBean
					// 這就是Spring的一種設計理念,實作了PriorityOrdered介面的Bean被認為是一種
					// 最高優先級的 Bean,這一類的Bean在進行為了完成裝配而去檢查型別時,
					// 不去檢查 factoryBean
					// 具體可以參考PriorityOrdered介面上的注釋檔案
					boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
					// 將引數封裝成為一個依賴描述符
					// 依賴描述符會通過:依賴所在的類,欄位名/方法名,依賴的具體型別等來描述這個依賴
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
					/**
					 * 決議指定beanName的屬性所匹配的值,并把決議到的屬性名存盤在autowiredBeanNames中,
					 * 當屬性存在多個封裝bean時,如:
					 * @Autowire
					 * private List<A> list;
					 * 將會找到所有匹配A型別的bean并將其注入
					 * 決議依賴,這里會處理@Value注解
					 * 另外,通過指定的型別到容器中查找對應的bean
					 */
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
					if (autowiredArgument != null) {
						// 將查找出來的依賴屬性添加到pvs中,后面會將這個pvs應用到bean上
						pvs.add(propertyName, autowiredArgument);
					}
					// 注冊bean直接的依賴關系
					for (String autowiredBeanName : autowiredBeanNames) {
						//注冊依賴
						registerDependentBean(autowiredBeanName, beanName);
						if (logger.isTraceEnabled()) {
							logger.trace("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);
			}
		}
	}

屬性依賴都獲取完了,接下來就是按部就班的進行注入了,

跟進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.
	 *
	 * 應用給定的屬性值,決議對此 bean 工廠中其他 bean 的任何運行時參考,必須使用深拷貝,所以我們不會永久修改這個屬性,
	 *
	 * @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) {
			mpvs = (MutablePropertyValues) pvs;
			if (mpvs.isConverted()) {
				// Shortcut: use the pre-converted values as-is.
				// 快捷方式:按原樣使用轉換前的值,
				try {
					bw.setPropertyValues(mpvs);
					return;
				}
				catch (BeansException ex) {
					throw new BeanCreationException(
							mbd.getResourceDescription(), beanName, "Error setting property values", ex);
				}
			}
			original = mpvs.getPropertyValueList();
		}
		else {
			original = Arrays.asList(pvs.getPropertyValues());
		}

		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}
		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) {
			if (pv.isConverted()) {
				deepCopy.add(pv);
			}
			else {
				String propertyName = pv.getName();
				Object originalValue = https://www.cnblogs.com/codegitz/p/pv.getValue();
				if (originalValue == AutowiredPropertyMarker.INSTANCE) {
					Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
					if (writeMethod == null) {
						throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
					}
					originalValue = https://www.cnblogs.com/codegitz/p/new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
				}
				// 給定一個 PropertyValue,回傳一個值,必要時決議對工廠中其他 bean 的任何參考
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					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.
				// 可能將轉換后的值存盤在合并的 bean 定義中,以避免對每個創建的 bean 實體進行重新轉換,
				if (resolvedValue == originalValue) {
					if (convertible) {
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				}
				else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					pv.setConvertedValue(convertedValue);
					deepCopy.add(pv);
				}
				else {
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		// 設定我們的(可能是經過按摩的)深拷貝,
		try {
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName,"Error setting property values", ex);
		}
	}
呼叫初始化方法

初始化方法的呼叫邏輯在initializeBean(beanName, exposedObject, mbd)里面,跟進代碼查看,

一看是不是很清晰,所以以后再遇到問你啥啥啥方法先執行,直接叼面試官,

	/**
	 *
	 * initializeBean()方法依次呼叫四個方法
	 * 1.invokeAwareMethods()
	 * 2.applyBeanPostProcessorsBeforeInitialization()
	 * 3.invokeInitMethods()
	 * 4.applyBeanPostProcessorsAfterInitialization()
	 *
	 */
	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// 1.先呼叫實作 aware 介面的方法
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// 2.呼叫 BeanPostProcessor#postProcessBeforeInitialization()方法
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// 3.呼叫初始化方法,例如實作了 InitializingBean#afterPropertiesSet() 方法
			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()) {
			// 4.最后呼叫 BeanPostProcessor#postProcessAfterInitialization()
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}
回圈依賴檢查

這一步主要是實作一個兜底的檢測,避免出現注入了一個本該被代理的但是卻注入了一個原生bean的情況,這部分會在回圈依賴的文章里結合來分析,

先看下代碼,

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			//earlySingletonReference只有在檢測到回圈依賴的情況下才不為空
			if (earlySingletonReference != null) {
				//如果exposedObject沒有在初始化方法中被改變,也就是沒有被增強
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					//檢測依賴
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					/**
					 * 因為bean創建完成后,其依賴的bean也一定是創建完成的
					 * 如果actualDependentBeans不為空,則說明依賴的bean還沒有被完全創建好
					 * 也就是說還存在回圈依賴
					 */
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}
注冊 DisposableBean

這也是一個回呼的操作,注冊一些銷毀的方法,Spring 中不但提供了對于初始化方法的擴展人口 , 同樣也提供了銷毀方法的擴展入口,對 于銷毀方法的擴展,除了我們熟知的配置屬性 destroy-method 方法外,用戶還可以注冊后處理器 DestructionAwareBeanPostProcessor 來統一處理 bean 的銷毀方法,跟進代碼registerDisposableBeanIfNecessary()

	/**
	 * Add the given bean to the list of disposable beans in this factory,
	 * registering its DisposableBean interface and/or the given destroy method
	 * to be called on factory shutdown (if applicable). Only applies to singletons.
	 *
	 * 將給定的 bean 添加到該工廠的一次性 bean 串列中,
	 * 注冊其 DisposableBean 介面和或在工廠關閉時呼叫的給定銷毀方法(如果適用),僅適用于單例,
	 *
	 * @param beanName the name of the bean
	 * @param bean the bean instance
	 * @param mbd the bean definition for the bean
	 * @see RootBeanDefinition#isSingleton
	 * @see RootBeanDefinition#getDependsOn
	 * @see #registerDisposableBean
	 * @see #registerDependentBean
	 */
	protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
			if (mbd.isSingleton()) {
				// Register a DisposableBean implementation that performs all destruction
				// work for the given bean: DestructionAwareBeanPostProcessors,
				// DisposableBean interface, custom destroy method.
				// 注冊一個為給定 bean 執行所有銷毀作業的 DisposableBean 實作:DestructionAwareBeanPostProcessors、DisposableBean 介面、自定義銷毀方法,
				/**
				 * 單例模式下需要銷毀的bean,此方法中會處理實作DisposableBean的bean
				 * 并且對所有的bean使用DestructionAwareBeanPostProcessors處理
				 * DisposableBean DestructionAwareBeanPostProcessors
				 */
				registerDisposableBean(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
			else {
				// A bean with a custom scope...
				// 自定義scope處理
				Scope scope = this.scopes.get(mbd.getScope());
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
				}
				scope.registerDestructionCallback(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
		}
	}

總結

本文主要分析了doCreateBean()方法,但是講得比較粗糙,回憶一下本文的思路,首先是通過類名反射得到一個class物件,然后推斷建構式去實體化得到一個bean物件,當然這部分沒有深入細節去說,分多了一篇文章,然后通過byNamebyType兩種方式去獲取依賴注入,之后通過反射將屬性注入到物件中,除去一些邊邊角角的校驗,總的思路就是這樣,還是相對清晰的,就是細節比較多,

這里牽扯的東西比較多,也算是Ioc里面比較難啃的部分了,我回看一遍我寫的文章,覺得整體言不達意,腦子里想十分,說出來可能只有六分,寫出來的就剩三分了,

個人水平有限,如有錯誤,還請指出,

如果有人看到這里,那在這里老話重提,與君共勉,路漫漫其修遠兮,吾將上下而求索,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/484467.html

標籤:Java

上一篇:int和Integer比較

下一篇:123取反操作為什么是-124 取反操作

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more