Spring5 源碼分析-容器刷新-@Import(ImportBeanDefinitionRegistrar)與@Bean以及父配置類

上一篇:Spring5 源碼分析-容器刷新-@Import(類 implments DeferImportSelector)java

原本是要將@Import(ImportBeanDefinitionRegistrar) @Bean @Bean接口方法他們解析與執行所在源碼位置幾乎是在一塊兒的,因此一塊兒寫在一篇博客中git

該方法最後結束的地方是獲取父類的class返回繼續調用doProcessConfigurationClass方法,嘗試父類class是否是也是配置類,若是是則繼續處理,一直循環直到沒有父類才結束。spring

功能說明

    @Import(ImportBeanDefinitionRegistrar) :將自定義的ImportBeanDefinitionRegistrar經過@Import導入,能夠實現自定義註冊BeanDefinition,甚至能夠根據一些配置手動干預生成的BeanDefinition。好比自定義一個註解(該註解能夠定義BeanDefinition的相關屬性),將@Import做爲元註解,那麼在registryBeanDefinition的時候就能夠根據自定義註解上的屬性設置想要註冊的BeanDefinition源碼分析

    @Bean:將類或者接口中被@Bean修飾的方法返回類的BeanDefinition註冊到容器中測試

    配置類父類:doProcessConfigurationClass方法結束的地方是獲取父類的SourceClass,將其返回並繼續調用doProcessConfigurationClass方法ui

舉例Demo

    這個例子中將涉及4中導入的方法:this

    1.經過自定義的註解導入自定義的ImportBeanDefinitionRegistrarlua

    2.類的@Bean methodspa

    3.接口的@Bean method.net

    4.父類也是配置類

接口(有@Bean)

public interface MyConfig {
	@Bean
	default City getCity(){
		return new City();
	}
}

配置類父類(添加@ComponentScan註解)

@ComponentScan(value = "com.jv.spring.other.scan")
public class MyParentConfig {
}

自定義導入功能註解

@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface MyAnnotation {
	boolean lazyInit();
}

自定義ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
	private AnnotationMetadata importMetadata;

	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(Dept.class);
		Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MyAnnotation.class.getName());
		boolean lazyInit = (boolean)annotationAttributes.get("lazyInit");
		bdb.getBeanDefinition().setLazyInit(lazyInit);
		registry.registerBeanDefinition("dept",bdb.getBeanDefinition());
	}
}

配置類

@Configuration
@PropertySource(value = "classpath:au.properties",name="my-au",encoding = "utf-8")
@MyAnnotation(lazyInit = true)
public class OtherConfig extends MyParentConfig implements MyConfig{

	@Bean
	public User getUser(){
		return new User();
	}
}

實體類

@Setter
@Getter
public class City {
	private String cityName = "重慶";
}
@Setter
@Getter
public class Dept {
	private String deptName = "研發部";
}
@Getter
@Setter
public class User {
	private String userName = "梅西";
}
@Component
@Getter
@Setter
public class Employee {
	private String empName = "Havi";
}

測試類

public class OtherMain {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(OtherConfig.class);
		User user = ac.getBean(User.class);
		System.out.println(user.getUserName());
		City city = ac.getBean(City.class);
		System.out.println(city.getCityName());
		Dept dept = ac.getBean(Dept.class);
		System.out.println(dept.getDeptName());
		Employee employee = ac.getBean(Employee.class);
		System.out.println(employee.getEmpName());
	}
}

測試結果

幾個對象都被Spring容器初始化成功,若是要看lazyInit效果,將斷點設置到getBean(Dept.class)這一行,看BeanFactory.singletonObjects中是否包含Dept對象。結論是不包含。如圖:

源碼分析

以上的操做主要集中在兩端代碼區域

掃描配置類,解析對應的註解(ConfigurationclassParser.doProcessConfigurationClass())

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {
		/**
		 * 首先處理@configuration配置類的嵌套類(嵌套類被@Component @Import @ImportResource @ComponentScan 或者這個類是否有被@Bean修飾的Method),
		 * 若是有則會進行遞歸調用processConfigurationClass()->doProcessConfigurationClass()
		 */
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass);
		}

		// Process any @PropertySource annotations  處理@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");
			}
		}

		// Process any @ComponentScan annotations  處理@ComponentScan註解,完成符合Spring規則類定義註冊
		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) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately  當即執行掃描
				//不會掃描到本身(@Configuration修飾了的類)
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				// 檢查掃描到全部類中是否還有被@Configuration修飾的類,若是還有,則須要再按照配置類的流程處理一遍
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						//若是掃描的包路徑下還有被@Configuration修飾的類,則遞歸進行處理
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations  處理@Import註解
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations  處理@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);
			}
		}

		// Process individual @Bean methods  處理@Bean註解
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces 處理接口默認方法上的@Bean註解
		// 根據接口的繼承接口遞歸調用retrieveBeanMethodMetadata(),將Interface中默認方法上的@Bean對應的beanMethod添加到configClass.beanMethods當中
		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;
	}

 

將掃描解析的結果(添加到對應的待處理列表)進行處理(上面的方法處理返回到)

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		。。。。省略若干行。。。。
		do {/*這裏會將符合規則的BD放入到BeanDefinitionMap中,還有配置類上的屬性文件放入到enviremont中,在使用AutowireAnnotationBeanPostProcessor的時候進行屬性注入*/
			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());
			}
			// 處理@Bean所註冊的beanMethod,解析成對應的BeanDefinition並註冊
			// 處理由@Import導入的ImportBeanDefinitionRegistrar
			// 處理@ImportResource導入的配置文件
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			。。。。省略若干行。。。。
	}

其中圈出來部分就是處理ImportBeanDefinitionRegistrar列表和BeanMethod,以及@ImportResource。再往loadBeanDefinitions()裏面翻,直到以下方法loadBeanDefinitionsForConfigurationClass()

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(普通方法+接口默認方法)添加的beanMethod
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
		//導入外部配置文件xml或者groovy
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		//導入@Import註解指定的相關配置類(由ImportBeanDefinitionRegistrar)
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

再往深層次的方法註釋,等我把源碼上傳到gitee當中再上連接

相關文章
相關標籤/搜索