主頁 > 後端開發 > Spring Ioc原始碼分析系列--Ioc容器注冊BeanPostProcessor后置處理器以及事件訊息處理

Spring Ioc原始碼分析系列--Ioc容器注冊BeanPostProcessor后置處理器以及事件訊息處理

2022-05-19 13:09:25 後端開發

Spring Ioc原始碼分析系列--Ioc容器注冊BeanPostProcessor后置處理器以及事件訊息處理

前言

上一篇分析了BeanFactoryPostProcessor的作用,那么這一篇繼續在refresh()方法里游蕩,相信對Spring熟悉點的朋友,在看完BeanFactoryPostProcessor后,一定會想到Spring里面還有個BeanPostProcessor,那這個東西是什么作用呢?下面會進行介紹,同時由于注冊BeanPostProcessor的邏輯比較簡單,這里會穿插一下BeanPostProcessor生效的時機和原始碼邏輯,實際上這部分應該是Bean實體化出現的邏輯,

介紹完這部分之后,會介紹Spring的訊息源初始化、廣播器的初始以及監聽器的初始化,這部分作業完成之后,Spring容器就會進入到Bean的創建程序,因為準備作業已經做得差不多了,容器已經準備好,接下來就是初始化Bean放進去容器里面,

BeanFactoryPostProcessor和BeanPostProcessor之間的區別

這兩個的區別還是很顯而易見的,主要表現在應用的階段不同BeanFactoryPostProcessor是對BeanDefinition直接生效的,這更加底層,也更加原始,所以直接使用BeanFactoryPostProcessor會比較少,BeanPostProcessor是對bean實體生效的,相對于對BeanDefinition的處理,這個階段更加靠后,BeanFactoryPostProcessor階段bean是尚未初始化出來的,BeanPostProcessor處理的時候已經生成了實體物件,BeanPostProcessor會在物件的實體基礎上進行一個更進一步的加工,

不熟悉的朋友看起來可能有點抽象,那么這里舉一個例子吧,

BeanFactoryPostProcessor的類比:

假設你要造一個杯子,那么杯子需要一份原材料串列,材質你可以選擇鐵、銅、金、銀等等,樣式你可以選擇圓型、方形、橢圓等等,假設開始原料選擇鐵,形狀為圓形,那么這一份原料串列對應的就是一個BeanDefinition,原料串列出來后,沒什么問題就會按照這一份串列去創建一個杯子,但是有時候需要一些額外的操作,例如對某些BeanDefinition進行檢查,假設有一個檢查員BeanFactoryPostProcessor去檢查每個BeanDefinition,他看到杯子的材質是鐵,覺得有失身份,于是把材料改成了金子,于是后續再去創建杯子的時候,就是個金杯了,

BeanPostProcessor的類比:

BeanPostProcessor的處理階段則要靠后,在上面杯子創建完成之后,才到了BeanPostProcessor出場,BeanPostProcessor會在實體的基礎上進行一些加工,拿杯子來舉例,上一個階段拿到的是一個粗糙的杯子,這里會進行一些處理,例如給杯子加點花紋樣式,給杯子拋光等等,**注意這些操作都是在一個已有的杯子上進行的,但是請注意,這不是絕對的,**BeanPostProcessor除了能對Bean進行深加工外,還能直接進行Bean替換,類比來說,就是換了個杯子,偷梁換柱,Spring Aop的功能就是這樣實作的,把經過代理的Bean放了進去,替換了原有的Bean,

所以比較一下得出一個很明顯的結論:

  • BeanFactoryPostProcessor對BeanDefinition生效

  • BeanPostProcessor對bean實體生效

原始碼分析

registerBeanPostProcessors(beanFactory)

話不多說,下面繼續分析refresh()方法里面的子方法,上一篇分析到了第五個子方法,那這篇從第六個registerBeanPostProcessors(beanFactory)開始,

跟進代碼,可以看到實作都委托給了PostProcessorRegistrationDelegate#registerBeanPostProcessors(beanFactory, this)方法,

	/**
	 * Instantiate and register all BeanPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before any instantiation of application beans.
	 *
	 * 實體化并注冊所有 BeanPostProcessor bean,如果給定順序,則按照順序排序,
	 * <p>必須在應用程式 bean 的任何實體化之前呼叫,
	 */
	protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
	}

繼續跟進,這個方法的邏輯也是比較簡單的,跟上篇的BeanFactoryPostProcessor注冊類似,這里也會按照優先級去對BeanPostProcessor進行排序然后按順序進行注冊,都是些家常套路了,可以跟著注釋去看一下,值得注意的是,這里會額外加入兩個BeanPostProcessor,分別為BeanPostProcessorCheckerApplicationListenerDetectorBeanPostProcessorChecker主要是用來記錄一些日志,ApplicationListenerDetector是用來檢測實作了ApplicationListener但是getBeanNamesForType()沒探測出來的漏網之魚,這里的漏網之魚可能是一些動態注冊的bean或者一些內部類,這里再次獲取后會放入到applicationListeners集合里,

	public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		// 獲取容器中所有的 BeanPostProcessor
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		// 注冊 BeanPostProcessorChecker,當 bean 不符合所有 BeanPostProcessor 處理的條件時,它會在 BeanPostProcessor 實體化期間創建 bean 時記錄一條資訊訊息
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		// 按照順序分類區分 BeanPostProcessor
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		// 首先注冊實作了 PriorityOrdered 介面的 BeanPostProcessor
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		// 其次注冊實作了 Ordered 介面的 BeanPostProcessor
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		// 現在到了注冊沒有實作上述介面的 BeanPostProcessor
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		// 最后,重新注冊所有內部 BeanPostProcessor MergedBeanDefinitionPostProcessor,
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		// 重新注冊用于將內部 bean 檢測為 ApplicationListeners 的后處理器,將其移動到處理器鏈的末尾(用于拾取代理等),
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

initMessageSource()

接下來繼續進行下一步的準備作業,初始化訊息源,這里是默認使用了父類的訊息源,如果沒有就初始化一個DelegatingMessageSource,這個DelegatingMessageSource會默認將所有的呼叫都委派到父容器的訊息源去決議,如果沒有父容器的訊息源,那么它不會決議任何訊息,

	/**
	 * Initialize the MessageSource.
	 * Use parent's if none defined in this context.
	 * 初始化訊息源,如果沒有在此背景關系中定義,則使用父容器的,
	 */
	protected void initMessageSource() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		// 回傳當前容器職工是否存在 messageSource,忽略祖先容器
		if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
			this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
			// Make MessageSource aware of parent MessageSource.
			// 如果存在祖先,并且 messageSource 型別是 HierarchicalMessageSource,則獲取祖先的 messageSource 設定到當前 messageSource 里,
			if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
				HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
				if (hms.getParentMessageSource() == null) {
					// Only set parent context as parent MessageSource if no parent MessageSource
					// registered already.
					hms.setParentMessageSource(getInternalParentMessageSource());
				}
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Using MessageSource [" + this.messageSource + "]");
			}
		}
		else {
			// Use empty MessageSource to be able to accept getMessage calls.
			// 本地不存在 messageSource,使用空 MessageSource 能夠接受 getMessage 呼叫
			DelegatingMessageSource dms = new DelegatingMessageSource();
			dms.setParentMessageSource(getInternalParentMessageSource());
			this.messageSource = dms;
			beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
			}
		}
	}

initApplicationEventMulticaster()

初始化 ApplicationEventMulticaster,如果背景關系中沒有定義,則使用 SimpleApplicationEventMulticaster

	/**
	 * Initialize the ApplicationEventMulticaster.
	 * Uses SimpleApplicationEventMulticaster if none defined in the context.
	 *
	 * 初始化 ApplicationEventMulticaster,
	 * 如果背景關系中沒有定義,則使用 SimpleApplicationEventMulticaster,
	 *
	 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
	 */
	protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		// 如果本地容器里存在 applicationEventMulticaster,直接使用本地容器里的 applicationEventMulticaster
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
			// 否則使用 SimpleApplicationEventMulticaster 廣播器
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			// 將給定的單例物件添加到該工廠的單例快取中
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

這個事件廣播器是干什么的呢?其實很簡單,就是把一個事件廣播到所有的ApplicationListener上,可以看一下里面的關鍵方法SimpleApplicationEventMulticaster#multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType),這里就是獲取所有的listener,如果有異步執行緒池,則異步執行,否則逐個呼叫,

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		// 決議事件的型別,這個type會用于后續的 ListenerCacheKey 快取 key 構建
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		// 獲取執行緒池
		Executor executor = getTaskExecutor();
		// 逐個廣播事件到 listener,就是將 listener 都遍歷呼叫一遍
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		// 錯誤處理器,記錄任務處理期間發生的錯誤
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}

	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// 將事件傳入listener,完成事件的監聽回呼
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			// ...
		}
	}

onRefresh()

這是個空方法,交給子類實作,這里可以用來初始化特定背景關系子類中的其他特殊 bean,也是留出來的一個擴展口,

	/**
	 * Template method which can be overridden to add context-specific refresh work.
	 * Called on initialization of special beans, before instantiation of singletons.
	 *
	 * 可以重寫以添加特定于背景關系的重繪作業的模板方法,在單例實體化之前呼叫特殊 bean 的初始化,
	 * 
	 * <p>This implementation is empty.
	 * @throws BeansException in case of errors
	 * @see #refresh()
	 */
	protected void onRefresh() throws BeansException {
		// For subclasses: do nothing by default.
	}

registerListeners()

上一步已經初始化完成了廣播器,那接下來就是檢查偵聽器并注冊它們,

事件可以按照注冊的型別進行區分,可以分為以下三種:

  • 通過addApplicationListener()手動添加進去的
  • 容器里實作了ApplicationListener介面的
  • 容器啟動早期需要的事件earlyApplicationEvents,早期事件是需要在這里直接發布的
	/**
	 * Add beans that implement ApplicationListener as listeners.
	 * Doesn't affect other listeners, which can be added without being beans.
	 *
	 * 添加實作 ApplicationListener 的 bean作為偵聽器,
	 * 不影響其他監聽器,可以添加而不是 bean,
	 */
	protected void registerListeners() {
		// Register statically specified listeners first.
		// 首先注冊靜態指定的監聽器,也就是通過addApplicationListener(ApplicationListener<?> listener) 注冊的listener,
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let post-processors apply to them!
		// 這里只是獲取beanName,是為了避免初始化 bean 導致后置處理器失效
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		// 逐個注冊listener
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// Publish early application events now that we finally have a multicaster...
		// 發布早期應用程式事件,因為我們終于有了一個廣播器......
		// 忍辱負重,早期事件存到了這里才能進行發布,因為之前沒有廣播器
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (earlyEventsToProcess != null) {
			// 逐個發布事件
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

finishBeanFactoryInitialization(beanFactory)

準備作業已經基本完成,接下來就到了finishBeanFactoryInitialization(beanFactory)方法了,從方法名可以看到,這個方法是負責完成此背景關系的 bean 工廠的初始化,初始化所有剩余的單例 bean,可以看到這個方法開始也進行了一些準備作業,例如注冊型別裝換器、占位符處理器以及LoadTimeWeaverAware加載等,最后會呼叫beanFactory.preInstantiateSingletons()進行物件創建,由于這里是比較復雜的程序,會分幾篇文章去詳細分析,這篇文章就是大概從表面上走完refresh()方法的原始碼,

	/**
	 * Finish the initialization of this context's bean factory,
	 * initializing all remaining singleton beans.
	 *
	 * 完成此背景關系的 bean 工廠的初始化,初始化所有剩余的單例 bean,
	 * 
	 */
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		// 初始化一個ConversionService用于型別轉換,這個ConversionService會在實體化物件的時候用到
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		// 添加一個StringValueResolver,用于處理占位符,可以看到,默認情況下就是使用環境中的屬性值來替代占位符中的屬性
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		// 創建所有的LoadTimeWeaverAware
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		// 靜態織入完成后將臨時的類加載器設定為null,所以除了創建LoadTimeWeaverAware時可能會用到臨時類加載器,其余情況下都為空
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		// 將所有的配置資訊凍結
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		// 開始進行真正的創建
		beanFactory.preInstantiateSingletons();
	}

finishRefresh()

到這里容器已經準備好了,bean也已經實體化完成,就差最后的一些事件通知和后續的兜底處理,這里比較重要的是會呼叫到所有實作了LifecycleProcessor#onRefresh()的Bean,在這里可以讓生命周期Bean實作很多擴展,其次比較重要的是會發布一個ContextRefreshedEvent事件,通知所有監聽器容器已經啟動完成,這里就可以實作一些容器啟動完成后的回呼或者是一些任務等,任君發揮,

	/**
	 * Finish the refresh of this context, invoking the LifecycleProcessor's
	 * onRefresh() method and publishing the
	 * {@link org.springframework.context.event.ContextRefreshedEvent}.
	 *
	 * 完成容器的重繪啟動,呼叫所有 LifecycleProcessor#onRefresh() 方法來發布 ContextRefreshedEvent 事件
	 *
	 */
	protected void finishRefresh() {
		// Clear context-level resource caches (such as ASM metadata from scanning).
		// 清除容器背景關系級別的資源快取(例如ASM掃描的元資料)
		clearResourceCaches();

		// Initialize lifecycle processor for this context.
		// 初始化背景關系的 lifecycle processor
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		// 首先將重繪傳播到生命周期處理器,
		getLifecycleProcessor().onRefresh();

		// Publish the final event.
		// 發布最終事件,
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		// 參與 LiveBeansView MBean(如果處于活動狀態),
		LiveBeansView.registerApplicationContext(this);
	}

總結

本文的重點有點分散,更像是走馬觀花,但是分散里的重點毫無疑問是理解和區分BeanFactoryPostProcessorBeanPostProcessor之間的區別,文章開頭通過一個例子去類比了一下這二者的作用階段和分別可以完成什么作業,個人覺得還是比較貼切的,希望能夠幫助到理解,

到這里已經基本把refresh()方法走了一遍,當然這里看到的大部分都是一些基礎準備作業,最關鍵的Bean實體化是還沒有開始分析的,Bean的實體化會后續分好幾篇文章繼續去分析,

今天這篇文章是比較簡單的,沒有太多邏輯,基本上都是一個一個小方法,嵌套不深,因為深入的我都不寫了哈哈,

這系列寫到這里,才完成了準備作業,接下來的Bean創建才是真正開始了重頭戲,那接下來繼續慢慢分析吧,

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

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

標籤:Java

上一篇:Go xmas2020 學習筆記 01-14 上篇

下一篇:JUC部分并發類使用方式

標籤雲
其他(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