SpringBoot启动

前言

之前已经介绍了SpringBoot jar启动过程。
现在我们来看SpringBoot入口类main方法的执行。

@SpringBootApplication
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        }
    }

一个最简洁的SpringBoot入口启动类就是如上的结构,只需要调用SPringApplication这个类中的run方法就可以了。那我们就以这个方法为入口来对启动过程进行分析。

SpringBoot启动过程

	public static ConfigurableApplicationContext run(Object source, String... args) {
		return run(new Object[] { source }, args);
	}
		public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}

我们发现会先调用SpringApplication的一个有参构造方法,参数为启动类Class对象。

	public SpringApplication(Object... sources) {
		initialize(sources);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void initialize(Object[] sources) {
	// 把sources设置到SpringApplication的sources属性中,目前只是一个MyApplication类对象
      }
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
		// 判断是否是web程序(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都必须可以被当前类加载器加载),并设置到webEnvironment属性中
		this.webEnvironment = deduceWebEnvironment();
		//从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化后设置到SpringApplication的initializers属性中。这个过程也就是找出所有的应用程序初始化器,这里用到了Springboot 的内部工具类 SpringFactoriesLoader
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		//同上找出Key为ApplicationListener的监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

获取到SpringApplication对象实例后,调用run()方法

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.started();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            //
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            //打印Banner
			Banner printedBanner = printBanner(environment);
            //创建SpringBoot上下文环境
			context = createApplicationContext();
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, ex);
			throw new IllegalStateException(ex);
		}
	}
  1. 先准备Environment,在prepareEnvironment()这个方法调用中,会在内部调用一次SpringApplication.run()
  2. 打印Banner图,这个我们在启动过程的日志中能够明显的看到。
  3. 创建SpringBoot程序上下文,在这个ApplicationContext中,会包含我们的SpringBoot启动类,在这里是MyApplication。因为我们已经在调用SpringApplication.run(MyApplication.class, args);显示的将启动类信息告诉了SpringApplication
  4. 刷新上下文,这个我们已经很熟悉了。也是我们今天要重点看的部分。这一步会将工程中的Bean都注入到Spring环境中。
  5. 后续的一些操作,在这里就不详细说了。

第4步的refreshContext(context);方法的执行过程中最终会调用AbstractApplicationContext.refresh()方法。

refresh过程

refresh()方法在之前在分析SpringMVC中已经分析过了。我们来看下载SpringBoot环境中,这个方法的各个步骤具体的作用。

prepareBeanFactory(beanFactory)

在这个方法中,进行后续的一些BeanFactory操作,在SpringBoot中,这部分会将一系PostProcessor处理器注入到Spring环境中。

invokeBeanFactoryPostProcessors(beanFactory)

接着会执行invokeBeanFactoryPostProcessors(beanFactory);方法,会初始化当前Spring环境中的所有BeanFactoryPostProcessor实现类,并且调用相应的处理方法。ConfigurationClassPostProcessor是最先被处理的BeanFactoryPostProcessor实现类(该类直接实现的是BeanDefinitionRegistryPostProcessor(BeanFactoryPostProcessor的子接口),在上面一步被注入到Spring环境中)。(具体的逻辑见PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()

		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);

		processConfigBeanDefinitions(registry);
	}

该方法主要就是调用processConfigBeanDefinitions(registry)方法来处理当前环境中带有@Configuration注解的BeanDefinition

	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//省略一大坨代码

		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(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();
            //检查是否在parse后有带来新的Bean
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<String>();
				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) {
			if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
				sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
			}
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}

1.先收集到带有@Configuration注解的Bean。

2.然后构造一个ConfigurationClassParser对象,来对上述符合条件的Bean进行处理,处理的过程是一个循环。

3.在parse过程中,被解析出来的类会放到configurationClasses属性中。然后使用ConfigurationClassBeanDefinitionReader去解析这些类,在解析过程中会出现有新的bean被Reader读取出来。在这个时候这些bean只是被加载到了Spring容器中。

3.检查是否在parse的过程中,是否有为当前环境带来新的Bean,如果有,找出新的Bean里面是否有带有@Configurationcandidates)的,如果有,那么继续进入下一次parse()的过程。直到没有新的candidates

看具体的parse过程

ConfigurationClassParser#doProcessConfigurationClass

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

		// 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.warn("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) {
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(
							holder.getBeanDefinition(), this.metadataReaderFactory)) {
						parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

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

		// Process any @ImportResource annotations
		if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
			AnnotationAttributes importResource =
					AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
			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.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.处理@PropertySources注解:进行一些配置信息的解析
2.处理@ComponentScan注解:使用ComponentScanAnnotationParser扫描basePackage下的需要解析的类(@SpringBootApplication注解也包括了@ComponentScan注解,只不过basePackages是空的,空的话会去获取当前@Configuration修饰的类所在的包),并注册到BeanFactory中(这个时候bean并没有进行实例化,而是进行了注册。具体的实例化在finishBeanFactoryInitialization方法中执行)。对于扫描出来的类,递归解析
3.处理@Import注解,具体下面会分析
4.处理@ImportResource注解:获取@ImportResource注解的locations属性,得到资源文件的地址信息。然后遍历这些资源文件并把它们添加到配置类的importedResources属性中
5.处理@Bean注解:获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中
6.处理DeferredImportSelector:处理第3步@Import注解产生的DeferredImportSelector,进行selectImports方法的调用找出需要import的类,然后再调用第3步相同的处理逻辑处理
这里@SpringBootApplication注解被@EnableAutoConfiguration修饰,@EnableAutoConfiguration注解被@Import(EnableAutoConfigurationImportSelector.class)修饰,所以在第3步会找出这个@Import修饰的类EnableAutoConfigurationImportSelector,这个类刚好实现了DeferredImportSelector接口,接着就会在第6步被执行。第6步selectImport得到的类就是自动化配置类。EnableAutoConfigurationImportSelector的selectImport方法会在spring.factories文件中找出key为EnableAutoConfiguration对应的值,有81个,这81个就是所谓的自动化配置类(XXXAutoConfiguration)。

最终对这些注解进行处理生成的数据,会在ConfigurationClassBeanDefinitionReaderloadBeanDefinitions方法中被处理。

重点来看下对@Import注解的处理,这个在SpringBoot环境中应用的非常多。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						ParserStrategyUtils.invokeAwareMethods(
								selector, this.environment, this.resourceLoader, this.registry);
						if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
							this.deferredImportSelectors.add(
									new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
						}
						else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					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 =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					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));
					}
				}
			}
			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();
			}
		}
	}

先递归找出所有的注解,然后再过滤出只有@Import注解的类,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解数据的话,首先发现@SpringBootApplication不是一个@Import注解,然后递归调用修饰了@SpringBootApplication的注解,发现有个@EnableAutoConfiguration注解,再次递归发现被@Import(EnableAutoConfigurationImportSelector.class)修饰,还有@AutoConfigurationPackage注解修饰,再次递归@AutoConfigurationPackage注解,发现被@Import(AutoConfigurationPackages.Registrar.class)注解修饰,所以@SpringBootApplication注解对应的@Import注解有2个,分别是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。找出所有的@Import注解之后,开始处理逻辑:

遍历这些@Import注解内部的属性类集合
1.如果这个类是个ImportSelector接口的实现类,实例化这个ImportSelector,如果这个类也是DeferredImportSelector接口的实现类,那么加入ConfigurationClassParser的deferredImportSelectors属性中让上面的第6步处理。否则调用ImportSelector的selectImports方法得到需要Import的类,然后对这些类递归做@Import注解的处理(实现类代表:AsyncConfigurationSelector
2.如果这个类是ImportBeanDefinitionRegistrar接口的实现类,设置到配置类的importBeanDefinitionRegistrars属性中。(实现类代表:MapperScannerRegistrar
3.其它情况下把这个类入队到ConfigurationClassParser的importStack(队列)属性中,然后把这个类当成是@Configuration注解修饰的类递归重头开始解析这个类(实现类代表:SchedulingConfiguration

registerBeanPostProcessors(beanFactory)

将所有的BeanPostProcessor实现类都注册到SpringBoot环境中

从Spring容器中找出的BeanPostProcessor接口的bean,并设置到BeanFactory的属性中。之后bean被实例化的时候会调用这个BeanPostProcessor。

该方法委托给了PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法执行。这里的过程跟invokeBeanFactoryPostProcessors类似:

先找出实现了PriorityOrdered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中
找出实现了Ordered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中
没有实现PriorityOrdered和Ordered接口的BeanPostProcessor加到BeanFactory的BeanPostProcessor集合中
这些已经存在的BeanPostProcessor在postProcessBeanFactory方法中已经说明,都是由AnnotationConfigUtils的registerAnnotationConfigProcessors方法注册的。这些BeanPostProcessor包括有AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)、RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法)、CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等。

如果是自定义的BeanPostProcessor,已经被ConfigurationClassPostProcessor注册到容器内。

这些BeanPostProcessor会在这个方法内被实例化(通过调用BeanFactory的getBean方法,如果没有找到实例化的类,就会去实例化)。

initMessageSource()

在Spring容器中初始化一些国际化相关的属性。

initApplicationEventMulticaster()

在Spring容器中初始化事件广播器,事件广播器用于事件的发布。

这个在分析SpringBoot监听器的时候会具体讲解。

####onRefresh()

一个模板方法,不同的Spring容器做不同的事情。

比如web程序的容器AnnotationConfigEmbeddedWebApplicationContext中会调用createEmbeddedServletContainer方法去创建内置的Servlet容器。

目前SpringBoot只支持3种内置的Servlet容器:

Tomcat
Jetty
Undertow

registerListeners()

把Spring容器内的时间监听器和BeanFactory中的时间监听器都添加的事件广播器中。

然后如果存在early event的话,广播出去。

finishBeanFactoryInitialization

实例化BeanFactory中已经被注册但是未实例化的所有实例(懒加载的不需要实例化)。

比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。

实例化的过程各种BeanPostProcessor开始起作用。

finishRefresh

refresh做完之后需要做的其他事情。

初始化生命周期处理器,并设置到Spring容器中(LifecycleProcessor)
调用生命周期处理器的onRefresh方法,这个方法会找出Spring容器中实现了SmartLifecycle接口的类并进行start方法的调用
发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
调用LiveBeansView的registerApplicationContext方法:如果设置了JMX相关的属性,则就调用该方法
发布EmbeddedServletContainerInitializedEvent事件告知对应的ApplicationListener进行响应的操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值