1. 程式人生 > >Spring component-scan原始碼分析(二) -- @Configuration註解處理

Spring component-scan原始碼分析(二) -- @Configuration註解處理

上篇文章Spring component-scan原始碼分析(一) – XML解析分析了Spring解析<context:component-scan …/>標籤時,把掃描到的合適的類封裝成BeanDefinition加入Sping容器中,本篇分析Spring如何解析帶相關注解的類。

從AnnotationConfigUtils的registerAnnotationConfigProcessors靜態方法入手

	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry,
@Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.
INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { //設定成ContextAnnotationAutowireCandidateResolver型別的 beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder>
beanDefs = new LinkedHashSet<>(8); //註冊ConfigurationClassPostProcessor類,解析@Configuration註解 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } //註冊AutowiredAnnotationBeanPostProcessor類 if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. //是否支援JSR-250 if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. //是否支援JPA if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } //註冊EventListenerMethodProcessor類 if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } //註冊DefaultEventListenerFactory類 if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }

該方法想Spring容器註冊了一些處理器,用於處理配置、注入等註解的處理

ConfigurationClassPostProcessor類

ConfigurationClassPostProcessor

可以看到,ConfigurationClassPostProcessor類實現了BeanDefinitionRegistryPostProcessor、PriorityOrdered這兩個介面。

(1). Spring在把所有bean解析封裝成BeanDefinition裝進容器後,在bean例項化之前,會例項化實現BeanDefinitionRegistryPostProcessor介面的bean,呼叫其postProcessBeanDefinitionRegistry方法,最後再呼叫BeanDefinitionRegistryPostProcessor介面的父類介面BeanFactoryPostProcessor介面的postProcessBeanFactory方法。這樣bean可以在這兩個方法中對容器中的bean屬性進行一些處理。 (2). 實現PriorityOrdered介面有最高優先順序,會優先例項化呼叫這樣的BeanDefinitionRegistryPostProcessor,它比Ordered介面優先順序高,而實現Ordered介面又比沒實現這兩個介面的優先順序高

1 postProcessBeanDefinitionRegistry方法

	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		...防止重複的處理
		//記錄處理過的registry
		this.registriesPostProcessed.add(registryId);
		//開始尋找符合的配置類
		processConfigBeanDefinitions(registry);
	}

processConfigBeanDefinitions方法開始處理@Configuration配置類

	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)) {
				//已經處理過了,處理過的bean會在其BeanDefinition中新增一個屬性作為標誌
				...省略log
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				//帶有@Configuration註解或帶有@Component、@ComponentScan、@Import、@ImportResource的註解或有帶@Bean的方法的類加入候選集合
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		//沒有候選的配置類,那不用處理了,收工
		if (configCandidates.isEmpty()) {
			return;
		}

		..省略@Order註解排序

		...省略名字生成器處理

		//environment沒被賦值就新建一個StandardEnvironment
		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 {
			//【標記1】開始解析
			parser.parse(candidates);
			//驗證解析得到的@Configuration類,由於CGLib的限制,類不能是final,並且@Bean方法要能被覆蓋
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);
			//reader是負責註冊beanDef進spring容器
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			//【標記2】開始向容器註冊BeanDefinition
			this.reader.loadBeanDefinitions(configClasses);
			//加入已解析過的快取中
			alreadyParsed.addAll(configClasses);
			//清空解析過的候選類
			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				//進入這裡,說明解析過程中,容器中新增了一些BeanDefinition
				...
				//一系列操作,提取出新的沒解析的類賦值給candidates,迴圈再次解析
			}
		}
		while (!candidates.isEmpty());

		//註冊ImportRegistry,用於處理ImportAware介面
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		...省略清理metadataReaderFactory的快取
	}

該方法程式碼很多,但邏輯還是清楚,主要從registry中篩選出@Configuration配置類進行解析,最後在把這些類封裝成BeanDefinition加入Spring容器。

核心程式碼是parser.parse(candidates)和this.reader.loadBeanDefinitions(configClasses);

【標記1】開始解析

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<>();

		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				//會進入該if
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				...
			}
			catch ...
		}
		//處理需要延遲引入的配置
		processDeferredImportSelectors();
	}

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		//把類的元資料和beanName封裝成ConfigurationClass
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}

在上篇文章Spring component-scan原始碼分析(一) – XML解析可以知道掃描帶有@Component註解的類會封裝成ScannedGenericBeanDefinition放入Spring容器。 ScannedGenericBeanDefinition

可以看到ScannedGenericBeanDefinition是實現了AnnotatedBeanDefinition介面的

	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		//處理@Conditional註解
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;//跳過(即不滿足條件)的話,直接返回
		}
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {//已經解析過
			if (configClass.isImported()) {//是被引入的
				if (existingClass.isImported()) {//舊的類也是被引入的
					existingClass.mergeImportedBy(configClass);//更新合併引入它配置類
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
				//否則就把舊的從解析快取中移除,也從父類快取中移除
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}
		//把configClass又封裝成SourceClass型別
		SourceClass sourceClass = asSourceClass(configClass);
		//會迴圈解析直到沒有符合的父類
		do {
			//解析@Configuration配置類
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
		//解析完裝入快取
		this.configurationClasses.put(configClass, configClass);
	}

前半部分程式碼會看不懂,先看doProcessConfigurationClass方法再回來看就明白了

	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {
		//是否有@Component註解
		if (sourceClass.getMetadata().isAnnotated(Component.class.getName())) {
			//處理成員內部類
			processMemberClasses(configClass, sourceClass);
		}

		// 處理@PropertySource註解
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			//StandardEnvironment實現了ConfigurableEnvironment介面
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				...省略log
			}
		}
		...省略處理@ComponentScans@ComponentScan註解,和處理XML配置處理差不多

		//處理@Import註解(用於引入其他配置類)
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		//處理@ImportResource註解(用於引入其他XML配置或.groovy檔案配置)
		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) {
				//處理${xxx}的情況
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				//把資源加入當前解析類的快取中
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		//拿到帶@Bean註解的方法,而且順序是和類中的原始碼一致
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			//封裝成BeanMethod,加入當前解析類快取中
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		//處理實現介面中的帶@Bean的預設方法(java8新增的default關鍵字)
		processInterfaces(configClass, sourceClass);

		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				//返回父類繼續解析
				return sourceClass.getSuperClass();
			}
		}
		//解析完成
		return null;
	}

解析步驟: 1、如果有@Component註解,先解析成員內部類; 2、處理@PropertySource註解; 3、處理@ComponentScans、@ComponentScan註解,和處理XML配置<context:component-scan …/>差不多; 4、處理@Import註解(用於引入其他配置類),其中涉及ImportSelector、DeferredImportSelector、ImportBeanDefinitionRegistrar介面的處理; 5、處理@ImportResource註解(用於引入其他XML配置或.groovy檔案配置); 6、處理帶@Bean註解的方法; 7、處理實現介面中的帶@Bean的預設方法(java8新增的default關鍵字); 8、如果有父類,且不是java原生類,且還沒解析過,就繼續解析父類

上面對帶@Configuration註解的類解析了一遍,解析的結果資訊都放在封裝的ConfigurationClass裡,接下來就是向Spring容器註冊BeanDefinition了

【標記2】開始向容器註冊BeanDefinition

	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
		//遍歷解析好的ConfigurationClass
		for (ConfigurationClass configClass : configurationModel) {
			//真正註冊邏輯在這
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}

	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);//從容器中移除該bean名字
			}
			//再從解析@Import得到的快取中移除當前類
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}

		if (configClass.isImported()) {
			//把當前的@Configuration類解析封裝成AnnotatedGenericBeanDefinition,加入spring容器
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			//解析載入帶@Bean註解的方法,會封裝成ConfigurationClassBeanDefinition或RootBeanDefinition(代理模式的情況下)放入Spring容器
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
		//載入@ImportResource註解配置檔案的bean,這就涉及到XML解析了
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources