Spring Ioc源碼分析系列--Ioc容器註冊BeanPostProcessor後置處理器以及事件消息處理

来源:https://www.cnblogs.com/codegitz/archive/2022/05/18/16285975.html
-Advertisement-
Play Games

Spring Ioc源碼分析系列--Ioc容器註冊BeanPostProcessor後置處理器以及事件消息處理 前言 上一篇分析了BeanFactoryPostProcessor的作用,那麼這一篇繼續在refresh()方法里游蕩,相信對Spring熟悉點的朋友,在看完BeanFactoryPost ...


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創建才是真正開始了重頭戲。那接下來繼續慢慢分析吧。

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前端處理二進位流數據--轉下載 導言 ​ 因業務需要,實現分類導出功能。篩選導出一定條件的數據,後端處理成Excle數據流,前端實現導出下載。 實現 方法一 ​ 將條件格式化成key=value&...文本格式,接到<a>標簽url介面之後,每當點擊導出按鈕的時候,創建一個<a>標簽,寫入介面地址+ ...
  • 本章是系列文章的第三章,介紹了基於數據流分析的一些優化方法。包括生命周期管理,可獲得表達式,常用表達式,可達性定義。本章在介紹這4中分析方法的基礎上提取出它們的通用模式。這一章形式化的內容比較多,看的時候有點燒腦,最好自己手工推導一下,要不然基本上看不懂:) 本文中的所有內容來自學習DCC888的學 ...
  • 一、吐槽 已經是凌晨12點了我還是睡不著 我所有的實體類時間用的j8的LocalDateTime 這就導致一個問題:jackson不能序列化時間,因為它不支持j8的Api,讓我添加 jackson-datatype-jsr310 解決 二、問題 如果是這樣做統一返回結果集需要 private sta ...
  • 知識回顧 解析完Bean信息的合併,可以知道Spring在實例化Bean之後,屬性填充前,對Bean進行了Bean的合併操作,這裡的操作主要做了對Bean對象標記了@Autowired、@Value、@Resource、@PostConstruct、@PreDestroy註解的欄位或者方法進行解析, ...
  • Cookied的設置會造成XSS攻擊,所以出現了防禦機制HttpOnly標誌(可選),客戶端腳本將無法訪問cookie(如果瀏覽器支持該標誌的話)。因此即使客戶端存在跨站點腳本(XSS)漏洞,瀏覽器也不會將Cookie透露給第三方。Cookie和Session後面還會設計到xss和csrf漏洞的利用... ...
  • 為了修複生產數據,需要執行一段一次性的代碼。 鑒於是spring老項目,就想到了InitializingBean。 代碼如下。服務啟動後,log里發現出現2條“一次性任務開始”。 好在裡面邏輯做了防重控制,沒有受到什麼影響。 @Slf4j @Component public class TransT ...
  • 什麼是隱藏類 隱藏類,是一種不能被其他類直接使用的類。引入隱藏類的主要目的是給框架來使用,使得框架可以在運行時生成類,並通過反射間接使用它們。可能有點抽象,不要緊,下麵我們通過一個例子來直觀的認識它! 隱藏類案例 第一步:先創建一個普通的Java類 public class JEP371Hidden ...
  • 下麵介紹的是JUC包下一些線程安全類的一些簡單使用和一些小demo。 Semaphore 信號量,即可以同時使用的線程數,tryrequire就是將信號量減一,release就是信號量+1,當等於0就會阻塞,大於零才會喚醒。 當需要控制線程訪問數量,可以使用信號量來做控制,比較簡單。 下麵是使用信號 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...