天天看點

[email protected]注解被掃面注射為bean的源碼解析(一)

一、入口

Springboot中@Configuration注解被掃描到的入口在AbstractApplicationContext類中的 invokeBeanFactoryPostProcessors(beanFactory) ; 方法中,方法上面有一行注解為 // Invoke factory processors registered as beans in the context.意思是把處理beanFactory的程式類型注冊為Context中的bean。

// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);
           

這句話的意思是spring-boot啟動添加不由容器管理的BeanFactoryPostProcessor,執行個體化後直接儲存在AbstractApplicationContext.beanFactoryPostProcessors。BeanFactoryPostProcessor包括以下 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ ConfigurationWarningsPostProcessor org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$ CachingMetadataReaderFactoryPostProcessor org.springframework.boot.context.config.ConfigFileApplicationListener$ PropertySourceOrderingPostProcessor

2、BeanFactoryPostProcessors

 在後面的invokeBeanFactoryPostProcessor()方法中,有兩個參數beanFactory和和beanFactoryPostProcessors。在beanFactoryPostProcessors中存在三個beanFactoryPostProcessor,分别是 1、ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor 這個類的作用是檢視@ComponentScan掃描的包有沒有問題,有的話則進行列印,在Spring-boot中這個類基本使用不到了,因為在@ SpringBootApplication注解中包含了 @ComponentScan (、 @SpringBootConfiguration、 @SpringBootConfiguration注解,是以這個類就不起作用了 2、 SharedMetadataReaderFactoryContextInitializer$ CachingMetadataReaderFactoryPostProcessor 3、 ConfigFileApplicationListener$ PropertySourceOrderingPostProcessor

然後去beanFactory中取出一個 BeanDefinitionRegistryPostProcessor類型的beanConfigurationClassPostProcessor,與beanfactory一起進入了方法 invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors , registry) ;

3、ConfigurationClassPostProcessor

上次放我們說 invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors , registry) ;方法中傳入了兩個參數一個是ConfigurationClassPostProcessor,一個是BeanFactory,那麼我們看下ConfigurationClassPostProcessor類,解析@Configuration注解的入口方法:processConfigBeanDefinitions,入參是beanFactory

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

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
           

首先是擷取beanFactory中加載的所有BeanDefinition:String[] candidateNames = registry.getBeanDefinitionNames(); 然後周遊操作,将我們項目啟動的入口bean,Applicaition類放入到 configCandidates集合中。 最後一Application類作為參數傳入到 ConfigurationClassParser類的parse()方法中。

// Process any @ComponentScan annotations
		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
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
           

然後在ConfigurationClassParser類中的 componentScanParser的parse方法,對@Component注解的**/*.class,即目前Application同級目錄下的類,進行掃描

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						if (isCandidateComponent(metadataReader)) {
           

4、@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

上面的注解是在SpringBootApplication中的注解,意思是Spring掃描的包中排除的類,其中 TypeExcludeFilter.class是排除特定類型的bean加入, AutoConfigurationExcludeFilter.class意思是,如果你自己聲明了一個DubboConfiguration然後也用pom檔案中配置了spring-dubbo-start啟用了帶有@AutoConfiguration注解的bean,那麼會忽略你自己寫的bean,但前提是,兩個@Configuration的類的名稱要一模一樣,否則不起作用

到這裡隻是說了Spring中怎麼排除的bean,還沒有講到在什麼地方加載的@Configuration注解,後面會接着開始說@Configuration'注解的bean是什麼時間被加載的

繼續閱讀