SpringBoot : 启动过程简析

837 阅读3分钟

1 前言

SpringBoot的启动,实际也就是启动了一个定制化的Spring容器。也就是创建了一个ConfigurableApplicationContext对象(AbstractApplicationContext的子类),然后执行refresh()方法。对于Spring的启动过程,可参考Spring加载过程及核心类,这里只简单分析一下SpringBoot如何初始化启动。为节省篇幅,代码仅抽取主要片段。

文中代码版本是2.1.10.RELEASE。

SpringBoot可以用下面代码的方式启动:

    public static void main(String[] args){
        SpringApplication app = new SpringApplication(MainApp.class);
        ApplicationContext ctx = app.run(args);
    }
  • 创建SpringApplication对象;
  • 执行SpringApplication.run(String... args)。

2 创建SpringApplication对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		
	// 判断应用类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
		
	// 查找并设置ApplicationContextInitializer
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		
	// 查找并设置监听器 ApplicationListener
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		
	// 推断启动类
	this.mainApplicationClass = deduceMainApplicationClass();
}

2.1 判断应用类型

SpringBoot有三种应用类型:

public enum WebApplicationType {
    NONE, // 非web应用
    SERVLET, // servlet web应用
    REACTIVE // reactive web应用
}

使用ClassUtils.isPresent判断哪些类可以加载到,进而判断是哪种类型的应用:

static WebApplicationType deduceFromClasspath() {
	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
		return WebApplicationType.REACTIVE;
	}
	for (String className : SERVLET_INDICATOR_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
	return WebApplicationType.SERVLET;
}

其中:

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

2.2 设置ApplicationContextInitializer

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

通过方法getSpringFactoriesInstances(Class<T> type)获取org.springframework.context.ApplicationContextInitializer的实例的集合。

2.3 设置ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

通过方法getSpringFactoriesInstances(Class<T> type)获取org.springframework.context.ApplicationListener的实例的集合。

2.4 getSpringFactoriesInstances

  • 1 执行SpringFactoriesLoader.loadFactoryNames读取文件META-INF/spring.factories中指定的接口的实现类的类名;
  • 2 执行createSpringFactoriesInstances实例化这些指定的类。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
		
	// 获取实现了type接口的类
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		
	// 实例化获取到的类
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

SpringFactoriesLoader.loadFactoryNames调用SpringFactoriesLoader.loadSpringFactories,获取到接口的实现类的类名:

Enumeration<URL> urls = (classLoader != null ?
			classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
			ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

其中:String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

2.5 META-INF/spring.factories

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer

# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

3 执行SpringApplication.run(String... args):

    public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		// 声明ConfigurableApplicationContext
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		
		/* 获取SpringApplicationRunListener实例集合,
		   并执行SpringApplicationRunListener.starting()方法。
		*/
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			
			// 实例化 ConfigurableApplicationContext
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
					
			/* 1 执行ApplicationContextInitializer.initialize
			   2 注册所有的BeanDefinition
			   3 执行SpringApplicationRunListener.contextPrepared/contextLoaded
			*/
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			
			// 执行refresh
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			
			// 执行所有的SpringApplicationRunListener.started方法
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		    //执行所有的SpringApplicationRunListener.running方法
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
        }

3.1 getRunListeners

private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

仍然是调用getSpringFactoriesInstancesMETA-INF/spring.factories中获取SpringApplicationRunListener的实现类并实例化。

3.2 createApplicationContext

createApplicationContext创建ConfigurableApplicationContext:

3.2.1 获取ConfigurableApplicationContext的实现类

根据在初始化时,获取到的WebApplicationType判断:

switch (this.webApplicationType) {
    case SERVLET:
	contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
	break;
    case REACTIVE:
	contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
	break;
    default:
	contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
    }

其中:

  • DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
  • DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework. boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"
  • DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"

这三个类都是org.springframework.context.ConfigurableApplicationContext的实现类。

3.2.2 实例化ConfigurableApplicationContext的实现类

(ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass)

通过org.springframework.beans.BeanUtils实例化获取到的ConfigurableApplicationContext的实现类。

3.3 prepareContext

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		
		// 执行前面设置的ApplicationContextInitializer集合的initialize方法
		applyInitializers(context);
		
		// 执行所有的SpringApplicationRunListener.contextPrepared方法
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		
		// 注册所有的BeanDefinition
		load(context, sources.toArray(new Object[0]));
		
		// 执行所有的SpringApplicationRunListener.contextLoaded方法
		listeners.contextLoaded(context);
	}

3.3.1 load(ApplicationContext context, Object[] sources)

protected void load(ApplicationContext context, Object[] sources){....}主要执行了:

  • BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
  • loader.load();

createBeanDefinitionLoader方法中创建并返回了一个BeanDefinitionLoader对象:

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
                //ClassPathBeanDefinitionScanner.scan方法扫描basePackage下的类,并生成相关beanDefinition。 
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}

参数sources是前面SpringApplication.run中通过getAllSources()获取后传过来的;

创建BeanDefinitionLoader对象,主要是创建了AnnotatedBeanDefinitionReaderXmlBeanDefinitionReader等对象,用来注册BeanDefinition。

BeanDefinitionLoader.load

根据不同的source类型,执行不同的BeanDefinition注册:

private int load(Object source) {
	Assert.notNull(source, "Source must not be null");
	if (source instanceof Class<?>) {
		return load((Class<?>) source);
	}
	if (source instanceof Resource) {
		return load((Resource) source);
	}
	if (source instanceof Package) {
		return load((Package) source);
	}
	if (source instanceof CharSequence) {
		return load((CharSequence) source);
	}
	throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

3.4 refreshContext

    private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

refresh(ApplicationContext)中,最终调用了AbstractApplicationContext.refresh()

// 调用了AbstractApplicationContext.refresh()
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

4 总结

  • 1 使用ClassUtils.isPresent来判断应用类型WebApplicationType
  • 2 在META-INF/spring.factories中定义各接口的实现类;
  • 3 调用getSpringFactoriesInstances通过META-INF/spring.factories读取到实现类后实例化;
  • 4 通过WebApplicationType判断ConfigurableApplicationContext的实现类,并调用BeanUtils实例化;
  • 5 ClassPathBeanDefinitionScanner.scan方法扫描basePackage下的类,并生成相关beanDefinition;
  • 6 执行ConfigurableApplicationContext.refresh()刷新Spring环境;
  • 7 通过AnnotatedBeanDefinitionReaderXmlBeanDefinitionReader等对象注册BeanDefinition