主頁 > 後端開發 > Spring IOC @Configuration注解分析

Spring IOC @Configuration注解分析

2023-06-08 08:08:20 後端開發

引入

在使用SpringBoot開發時,最常用的注解有@Component、@Service、@Controller、@Configuration等,當類使用這些注解標記時,類會被Spring IOC容器管理,包括創建,填充屬性和實體化,

但是Spring容器如何發現并將這些類放到容器進行管理呢?

今天這篇博客主要分析Spring如何處理@Configuration注解,

@Configuration定義

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
	@AliasFor(annotation = Component.class)
	String value() default "";
	
	//Since: 5.2
	boolean proxyBeanMethods() default true;

	//Since: 6.0
	boolean enforceUniqueMethods() default true;
}

看Configuration注解的定義,它本質也是一個@Component注解,

所以,標記有@Configuration注解的類在Spring注冊bean的第一階段就會被注冊到容器中,但是使用@Configuration注解的類內部的其他bean并沒有被注冊到容器中,

想知道@Configuration注解被如何處理,需要看invokeBeanFactoryPostProcessors 方法,

ConfigurationClassPostProcessor處理@Configuration注解

先大致說一下invokeBeanFactoryPostProcessors 方法的基本流程,因為這個方法會處理所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor介面的子類:

  • 先處理方法入參:beanFactoryPostProcessors(如果只是分析AnnotationConfigApplicationContext,這個引數是空的,只有在分析SpringBoot的啟動程序時,這個引數才會有值)
  • 再處理BeanDefinitionRegistryPostProcessor的子類
    • 這里最重要的一件事情是呼叫ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,
  • 最后處理BeanFactoryPostProcessor的子類

invokeBeanFactoryPostProcessors 的部分代碼:

public static void invokeBeanFactoryPostProcessors(
		ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
	
	Set<String> processedBeans = new HashSet<>();
	if (beanFactory instanceof BeanDefinitionRegistry) {
		//省略部分代碼,處理入參:beanFactoryPostProcessors

        //開始處理BeanDefinitionRegistryPostProcessor介面的子類
        //保存所有符合的子類,即實作BeanDefinitionRegistryPostProcessor,同樣實作PriorityOrdered
		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
        //呼叫currentRegistryProcessors中所有的BDRPP
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
		currentRegistryProcessors.clear();

		//省略部分代碼,同樣處理BeanDefinitionRegistryPostProcessor介面的子類
	}
	else {
		//省略部分代碼
	}
	//省略部分代碼 處理BeanFactoryPostProcessor介面的子類
}

以上是invokeBeanFactoryPostProcessors的部分代碼,接下來看ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,在這個方法里開始正式地處理@Configruation注解:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	String[] candidateNames = registry.getBeanDefinitionNames();

	//遍歷所有的候選者
	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		//校驗候選者類上是否使用了@Configuration注解
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}

	//如果configCandidates為空,則立即回傳
	if (configCandidates.isEmpty()) {
		return;
	}

	configCandidates.sort((bd1, bd2) -> {
		int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
		int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
		return Integer.compare(i1, i2);
	});

	SingletonBeanRegistry sbr = null;
	if (registry instanceof SingletonBeanRegistry) {
		sbr = (SingletonBeanRegistry) registry;
		if (!this.localBeanNameGeneratorSet) {
			BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
			if (generator != null) {
				this.componentScanBeanNameGenerator = generator;
				this.importBeanNameGenerator = generator;
			}
		}
	}

	if (this.environment == null) {
		this.environment = new StandardEnvironment();
	}

	// 創建一個決議@Configuration的決議器
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);
	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

	do {
		StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
		//開始決議候選類
		parser.parse(candidates);

		parser.validate();
		Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
		configClasses.removeAll(alreadyParsed);
		// Read the model and create bean definitions based on its content
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}

		//注冊決議后的類到容器中
		this.reader.loadBeanDefinitions(configClasses);

		alreadyParsed.addAll(configClasses);
		processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
		candidates.clear();
		if (registry.getBeanDefinitionCount() > candidateNames.length) {
			String[] newCandidateNames = registry.getBeanDefinitionNames();
			Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
			Set<String> alreadyParsedClasses = new HashSet<>();
			for (ConfigurationClass configurationClass : alreadyParsed) {
				alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
			}
			for (String candidateName : newCandidateNames) {
				if (!oldCandidateNames.contains(candidateName)) {
					BeanDefinition bd = registry.getBeanDefinition(candidateName);
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
							!alreadyParsedClasses.contains(bd.getBeanClassName())) {
						candidates.add(new BeanDefinitionHolder(bd, candidateName));
					}
				}
			}
			candidateNames = newCandidateNames;
		}
	}

	while (!candidates.isEmpty());
	// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
	if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
		sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
	}

	if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
		// Clear cache in externally provided MetadataReaderFactory; this is a no-op
		// for a shared cache since it'll be cleared by the ApplicationContext.
		((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
	}

}

這個方法的關鍵點:

  • 找到所有要決議的類
  • 創建一個決議者
  • 開始決議類
    • 記住,這里只是決議,也就是確認所有需要處理的類或者方法,這些類或者方法最終也會注冊到容器中
  • 注冊決議后的類到容器中:this.reader.loadBeanDefinitions(configClasses);

決議

那么實際上如何決議呢?

protected final SourceClass doProcessConfigurationClass(
		ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
		throws IOException {
	//1. 判斷當前類是否有內部類,如果有內部類是否使用了@Configuration注解,如果使用了該注解,那么需要先處理內部類的配置
	//存在這種可能,但是實際開發中很少使用
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		processMemberClasses(configClass, sourceClass, filter);
	}

	// 處理@PropertySource注解
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
		else {
			logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	// 處理@ComponentScan注解
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() &&
			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		for (AnnotationAttributes componentScan : componentScans) {
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// 處理@Import注解
	processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

	// 處理@ImportResource注解
	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	if (importResource != null) {
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		for (String resource : resources) {
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}

	// 處理使用了@Bean的方法
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}
	// Process default methods on interfaces
	processInterfaces(configClass, sourceClass);
	// Process superclass, if any
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !superclass.startsWith("java") &&
				!this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			// Superclass found, return its annotation metadata and recurse
			return sourceClass.getSuperClass();
		}
	}
	// No superclass -> processing is complete
	return null;
}
  • 確認當前類是否有內部類,內部類是否也使用了@Configuration注解
  • 處理@PropertySource注解
  • 處理@ComponentScan注解
  • 處理@Import注解
  • 處理@ImportResource注解
  • 處理@Bean注解

這里以@Import和@Bean為例進行分析:

@Configuration
@Import(MyBeanFactoryPostProcessor.class)
public class JavaConfig {
    @Bean
    public Country country() {
        return new Country();
    }
}

JavaConfig類上使用了@Configuration注解和@Import注解,在@Import注解引入了MyBeanFactoryPostProcessor類:

public class MyBeanFactoryPostProcessor implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry, importBeanNameGenerator);
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(Person.class);

        registry.registerBeanDefinition("person", rootBeanDefinition);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
    }
}

MyBeanFactoryPostProcessor實作了ImportBeanDefinitionRegistrar介面,

下面分析一下如何處理@Import注解:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
		boolean checkForCircularImports) {
	if (importCandidates.isEmpty()) {
		return;
	}
	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {

			//@Import注解的引數可能不止一個類,可以有多個
			for (SourceClass candidate : importCandidates) {
				//candidate繼承了ImportSelector
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
							this.environment, this.resourceLoader, this.registry);
					Predicate<String> selectorFilter = selector.getExclusionFilter();
					if (selectorFilter != null) {
						exclusionFilter = exclusionFilter.or(selectorFilter);
					}
					if (selector instanceof DeferredImportSelector) {
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
						processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
					}
				}
				//candidate繼承了ImportBeanDefinitionRegistrar
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();
					ImportBeanDefinitionRegistrar registrar =
							ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
									this.environment, this.resourceLoader, this.registry);

					//將candidate放到configClass中,后續進行處理
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				//candidate沒有繼承上述兩個類,按照@Configuration處理
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
				}
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		}
		finally {
			this.importStack.pop();
		}
	}
}

processImports方法主要處理兩種情況:

  • 實作ImportSelector介面的類
  • 實作ImportBeanDefinitionRegistrar介面的類

但是需要注意,這個方法只是把這些類放到了集合中,但是還沒有開始處理,

下面開始看如何處理使用@Bean的方法

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
		AnnotationMetadata original = sourceClass.getMetadata();
		//找到所有使用@Bean的方法
		Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
		if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
			try {
				AnnotationMetadata asm =
						this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
				Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
				if (asmMethods.size() >= beanMethods.size()) {
					Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
					for (MethodMetadata asmMethod : asmMethods) {
						for (MethodMetadata beanMethod : beanMethods) {
							if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
								selectedMethods.add(beanMethod);
								break;
							}
						}
					}
					if (selectedMethods.size() == beanMethods.size()) {
						// All reflection-detected methods found in ASM method set -> proceed
						beanMethods = selectedMethods;
					}
				}
			}
			catch (IOException ex) {
				logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
				// No worries, let's continue with the reflection metadata we started with...
			}
		}
		return beanMethods;
	}

最后回傳所有需要處理的方法,

注冊

private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

		if (trackedConditionEvaluator.shouldSkip(configClass)) {
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}

		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}

		//注冊使用@Bean的方法
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

		//注冊實作ImportBeanDefinitionRegistrar介面的類
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

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

標籤:其他

上一篇:【Python&RS】遙感影像的像素坐標轉地理坐標(仿射變換)

下一篇:返回列表

標籤雲
其他(160523) Python(38215) JavaScript(25478) Java(18209) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7235) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4585) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1983) 功能(1967) HtmlCss(1952) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(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
最新发布
  • Spring IOC @Configuration注解分析

    # 引入 在使用SpringBoot開發時,最常用的注解有@Component、@Service、@Controller、@Configuration等。當類使用這些注解標記時,類會被Spring IOC容器管理,包括創建,填充屬性和實體化。 但是Spring容器如何發現并將這些類放到容器進行管理呢 ......

    uj5u.com 2023-06-08 08:08:20 more
  • 【Python&RS】遙感影像的像素坐標轉地理坐標(仿射變換)

    ? GDAL(Geospatial Data Abstraction Library)是一個在X/MIT許可協議下的開源柵格空間資料轉換庫。它利用抽象資料模型來表達所支持的各種檔案格式。它還有一系列命令列工具來進行資料轉換和處理。 Python的GDAL庫作為柵格資料的處理轉換庫,其支持幾百種柵格數 ......

    uj5u.com 2023-06-08 08:08:15 more
  • 逍遙自在學C語言 | 宏定義技巧讓你的C代碼快人一步

    ## 前言 在C語言中,宏定義是一種預處理指令,用于在代碼中定義和使用常量、函式或代碼片段的替代。 宏定義使用`#define`關鍵字來定義,并在代碼中進行替換。宏定義具有以下優點: 1. **簡化代碼**:宏定義可以將一些常用的、重復出現的代碼片段簡化為一個宏名稱,提高代碼的可讀性和簡潔性。 2. ......

    uj5u.com 2023-06-08 08:08:04 more
  • IO流 p10 列印流

    # 列印流 PrintStream 和 PrintWriter ![](https://img2023.cnblogs.com/blog/3008601/202306/3008601-20230604103522664-997405676.png) ![](https://img2023.cnblo ......

    uj5u.com 2023-06-08 08:02:46 more
  • Python&Excel辦公自動化

    操作作業簿 01 新建一個excel作業簿 #2023-4-17 import xlwings as xw # 啟動 excel,但不新建作業簿 app是什么,app是excel程式本身 app = xw.App(visible=True,add_book=True) #新建一個作業簿 workbo ......

    uj5u.com 2023-06-08 07:57:40 more
  • Python 串列推導式:簡潔、高效的資料操作藝術

    # Python 串列推導式:簡潔、高效的資料操作藝術 Python 的串列推導式,這個看似簡單的語法糖,實則內含無限威力。在 Python 代碼撰寫中,串列推導式的靈活性和簡潔性讓它成為了不可或缺的一部分。在這篇文章中,我們將更全面、更深入地探討串列推導式,從基礎的概念認識,到各類進階的用法和操作 ......

    uj5u.com 2023-06-08 07:57:35 more
  • 鏈家廣州二手房資料 2023

    還記得在2019年的夏天曾經用 R 爬過一份廣州在 lianjia.com 放盤資料 ([博客1](https://www.cnblogs.com/yukiwu/p/10975337.html),[博客2](https://www.cnblogs.com/yukiwu/p/11271515.html ......

    uj5u.com 2023-06-08 07:57:29 more
  • jvm中類和物件定義存盤基礎知識

    在Java虛擬機中,類和物件是程式的基本組成單元。類定義了一組物件的共性特征和行為,是Java程式中最基本的代碼單元。而物件則是具體的實體,有自己獨特的狀態和行為。在JVM中,類和物件都需要進行存盤,因此了解類和物件的存盤基礎知識對于Java程式員來說是非常重要的。 ......

    uj5u.com 2023-06-08 07:57:25 more
  • 【python基礎】回圈陳述句-while回圈

    # 1.初識while回圈 回圈陳述句主要的作用是在多次處理具有相同邏輯的代碼時使用。while回圈是Python提供的回圈陳述句之一。 while回圈的語法格式之一: ![image](https://img2023.cnblogs.com/blog/3179433/202306/3179433-20 ......

    uj5u.com 2023-06-08 07:57:14 more
  • spring cloud gateway網關(一)之網關路由

    1、gateway相關介紹 在微服務架構中,系統往往由多個微服務組成,而這些服務可能部署在不同機房、不同地區、不同域名下。這種情況下,客戶端(例如瀏覽器、手機、軟體工具等)想要直接請求這些服務,就需要知道它們具體的地址資訊,例如 IP 地址、埠號等。這種客戶端直接請求服務的方式存在很多的復雜問題。 ......

    uj5u.com 2023-06-08 07:56:58 more