springboot源码分析:启动过程中需要特殊介绍的几个类

Scroll Down

1. @Import注解及ImportBeanDefinitionRegistrar

这个类的使用是在ServletWebServerFactoryAutoConfiguration这个类中遇到的,代码如下:

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

ImportBeanDefinitionRegistrar该类有以下几个功能:

ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类。

使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean。

实现该接口的类拥有注册bean的能力。

2. 自动配置中的ObjectProvider

@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
				ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
				ObjectProvider<TomcatContextCustomizer> contextCustomizers,
				ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
	TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
	factory.getTomcatConnectorCustomizers()
					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
	factory.getTomcatContextCustomizers()
					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
	factory.getTomcatProtocolHandlerCustomizers()
					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
	return factory;
}

依赖关系的改进
同样是在Spring 4.3版本中,不仅隐式的注入了单构造参数的属性。还引入了ObjectProvider接口。

ObjectProvider接口是ObjectFactory接口的扩展,专门为注入点设计的,可以让注入变得更加宽松和更具有可选项。

那么什么时候使用ObjectProvider接口?

如果待注入参数的Bean为空或有多个时,便是ObjectProvider发挥作用的时候了。

如果注入实例为空时,使用ObjectProvider则避免了强依赖导致的依赖对象不存在异常;如果有多个实例,ObjectProvider的方法会根据Bean实现的Ordered接口或@Order注解指定的先后顺序获取一个Bean。从而了提供了一个更加宽松的依赖注入方式。

Spring 5.1之后提供了基于Stream的orderedStream方法来获取有序的Stream的方法。

3. 关于AutoConfiguration

Springboot使用AutoConfiguration实现自动化配置,以提供即插即用的组件服务。其原理是在@SpringBootApplication中,标记@EnableAutoConfiguration。在标记为@EnableAutoConfiguration后,Springboot会自动开启AutoConfiguration。在标记为@EnableAutoConfiguration后,Springboot会自动开启AutoConfiguration自动配置。其功能实现入口是在@EnableAutoConfiguration标记的@Import(EnableAutoConfigurationImportSelector.class),通过@Import功能,导入ImportSelector,通过ImportSelector的selectImports功能,自动引入需要一些Class类型作为配置类的Bean生成。
在EnableAutoConfigurationImport的selectImports方法中,使用SpringFactoriesLoader.loadFactoryNames功能来获取spring.factories中的key。即org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类名,再通过过滤条件筛选出最终需要引入的所有类名,作为selectImports的返回值,如此Springboot就会把这个类作为配置类进行加载解析。

4. 关于ConfigurationClassPostProcessor 该部分内容已废弃

该部分内容因为不够完整,目前已废弃,参考Springboot SPI机制
该类实现了BeanFactoryPostProcessor,结构图如下:
image.png

会在Spring容器初始化的时候调用postProcessBeanFactory,然后在processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法内部创建ConfigurationClassParser,并调用该类的parse方法解析所有带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 (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
			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));
		}
	}

	// Return immediately if no @Configuration classes were found
	if (configCandidates.isEmpty()) {
		return;
	}

	// Sort by previously determined @Order value, if applicable
	configCandidates.sort((bd1, bd2) -> {
		int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
		int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
		return Integer.compare(i1, i2);
	});

	// Detect any custom bean name generation strategy supplied through the enclosing application context
	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();
	}

	// Parse each @Configuration class
	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 {
		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);

		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();
	}
}

逻辑如下:
(1)、首先获取所有的BeanDefinitionName,然后循环获取candidatesConfig,条件是BeanDefinition的Attribute属性里面是否存在CONFIGURATION_CLASS_ATTRIBUTE属性,如果存在,那么代表这个BeanDefinition已经被解析过了。否则就会走如下的逻辑
查找到被Configuration注解标记的类,代码如下:

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

	String className = beanDef.getBeanClassName();
	if (className == null || beanDef.getFactoryMethodName() != null) {
		return false;
	}

	AnnotationMetadata metadata;
	if (beanDef instanceof AnnotatedBeanDefinition &&
				className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
		// Can reuse the pre-parsed metadata from the given BeanDefinition...
		metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
	}
	else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
		// Check already loaded Class if present...
		// since we possibly can't even load the class file for this Class.
		Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
		if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
					BeanPostProcessor.class.isAssignableFrom(beanClass) ||
					AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
					EventListenerFactory.class.isAssignableFrom(beanClass)) {
			return false;
		}
		metadata = AnnotationMetadata.introspect(beanClass);
	}
	else {
		try {
			MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
			metadata = metadataReader.getAnnotationMetadata();
		}
		catch (IOException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Could not find class file for introspecting configuration annotations: " +
							className, ex);
			}
			return false;
		}
	}

	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (config != null || isConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		return false;
	}

	// It's a full or lite configuration candidate... Let's determine the order value, if any.
	Integer order = getOrder(metadata);
	if (order != null) {
		beanDef.setAttribute(ORDER_ATTRIBUTE, order);
	}

	return true;
}

具体的逻辑就是查看这个BeanDefinition是否被Configuration注解修饰。

parse方法的代码如下:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			if (bd instanceof AnnotatedBeanDefinition) {
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
			else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
			}
			else {
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
		}
	}

	this.deferredImportSelectorHandler.process();
}

这里面继续调用了processConfigurationClass这个方法来解析。

@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);
	}

	// Process any @PropertySource annotations
	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
	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());
			// Check the set of scanned definitions for any further config classes and parse recursively if needed
			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());
				}
			}
		}
	}

	// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);

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

(1)、首先这个方法会判断这个类是否被Component注解修饰,如果被修饰的话,会进入processMemberClasses逻辑。代码如下:

/**
 * Register member (nested) classes that happen to be configuration classes themselves.
 */
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
	Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
	if (!memberClasses.isEmpty()) {
		List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
		for (SourceClass memberClass : memberClasses) {
			if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
						!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
				candidates.add(memberClass);
			}
		}
		OrderComparator.sort(candidates);
		for (SourceClass candidate : candidates) {
			if (this.importStack.contains(configClass)) {
				this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
			}
			else {
				this.importStack.push(configClass);
				try {
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
				finally {
					this.importStack.pop();
				}
			}
		}
	}
}

这个类首先会判断这个source类里面是否定义了内部类且这个内部类是否被标记了@Configuration注解,如果也被标记了的话,那么会递归调用processConfigurationClass去尝试加载这个内部类的所有引用类。
(2)、接下来会尝试判断这个类是否标记了@PropertySource注解,如果标记了,那么会尝试加载这个注解标记的属性文件,并添加设置到environment中。
(3)、接下来会判断这个类是否标记了@ComponentScans注解,如果标记了,那么会尝试递归加载这个包下面所有被@Component类型的注解标记的类。注意会加载所有的类,不仅仅加载@Configuration。也就说部分被@Controller或者其他类型注解的类会被提前加载。
(4)、接下来会尝试处理被@Import注解内部设置的类型。比如这个直接内部存在ImportSelector接口的类型时,会自动创建这个类的实例,并执行这个接口的selectImports方法来获取本次需要导入的所有的类名,并创建这些类的SourceClass,然后继续循环递归扫描这些类下面的@Import注解。然后会调用processConfigurationClass这个方法。如果这个类没有任何以上的特殊操作那么就会被加载到configurationClasses容器中,等待被reader注册到容器中。如果@Import注解内部存在ImportBeanDefinitionRegistrar这个类型时,会尝试调用这个接口的registerBeanDefinitions直接向容器中注册。注册操作则是通过ConfigurationClassBeanDefinitionReader来负责调用的。
(5)、接下来会处理@ImportResource注解类型。来读取一些XML文件并加载。
(6)、接下来会加载在@Configuration类下面定义的被@Bean标记的方法。并添加到configClass中,等待被ConfigurationClassBeanDefinitionReader加载。
(7)、最后通过ConfigurationClassBeanDefinitionReader加载以上所有被扫描到的类,它们统一放在configurationClasses中。
至此由一个最开始的@SpringBootApplication注解标记的类触发所有各种形式的加载。Spring中通过spring.factories文件自动加载配置类的方式其实就是通过上述的方式自动加载到容器中的也叫SPI方式。

5. 关于ConfigurationClassBeanDefinitionReader

这个类可以通过类名就知道是用来加载被@Configuration的类型到容器中的。该类的初始化是在processConfigBeanDefinitions方法中,代码如下:

// 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());
}

通过构造函数引入了BeanDefinitionRegistry,使该类具备了向容器中注册BeanDefinition的能力。
入口方法如下:

/**
 * Read {@code configurationModel}, registering bean definitions
 * with the registry based on its contents.
 */
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	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);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}

	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

这个类的逻辑如下:
(1)、如果这个类已经被注册过了,则删掉。
(2)、首先注册被@Configuration标记的类。registerBeanDefinitionForImportedConfigurationClass
(3)、循环注册@Configuration类中被@Bean标记的方法,loadBeanDefinitionsForBeanMethod
(4)、注册被@ImportResource标记的文件。
(5)、执行ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法。