本文从SpringApplication类开始分析Spring Boot应用的启动过程,使用的Spring Boot版本是1.5.15.RELEASE。
成员变量
public class SpringApplication {
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
/**
* Default banner location.
*/
public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
/**
* Banner location property key.
*/
public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private static final Log logger = LogFactory.getLog(SpringApplication.class);
private final Set<Object> sources = new LinkedHashSet<Object>();
private Class<?> mainApplicationClass;
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
private boolean logStartupInfo = true;
private boolean addCommandLineProperties = true;
private Banner banner;
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
private boolean webEnvironment;
private boolean headless = true;
private boolean registerShutdownHook = true;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Map<String, Object> defaultProperties;
private Set<String> additionalProfiles = new HashSet<String>();
}
- DEFAULT_CONTEXT_CLASS常量表示非Web环境默认的应用上下文是AnnotationConfigApplicationContext类型;
- DEFAULT_WEB_CONTEXT_CLASS常量表示Web环境下默认的应用上下文是AnnotationConfigEmbeddedWebApplicationContext类型;
- sources表示bean来源;
- mainApplicationClass表示启动类类型;
- resourceLoader表示资源加载的策略;
- beanNameGenerator表示生成bean名称的策略;
- environment表示使用的环境;
- applicationContextClass表示创建的应用上下文的类型;
- webEnvironment表示当前是否是Web环境;
- initializers表示添加的ApplicationContextInitializer;
- listeners表示添加的ApplicationListener;
- ...
构造函数
SpringApplication类的两个构造函数如下,它们都调用initialize方法做初始化工作。
public SpringApplication(Object... sources) {
initialize(sources);
}
public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
this.resourceLoader = resourceLoader;
initialize(sources);
}
initialize方法代码如下:
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
该方法做了如下工作:
- deduceWebEnvironment方法检测当前是否是Web环境,只有当Servlet接口和ConfigurableWebApplicationContext接口都存在且能被加载时才是Web环境;
private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
- getSpringFactoriesInstances方法从jar包的META/spring.factories文件中获取特定类型的工厂实现类限定名并实例化它们,此处是创建了ApplicationContextInitializer和ApplicationListener两种类型的工厂实现类,并分别保存到initializers和listeners成员变量中。每个jar包都可能有spring.factories文件,以spring-boot-1.5.15.RELEASE.jar中的META/spring.factories为例,该文件包含4种ApplicationContextInitializer和9种ApplicationListener;
# 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.context.embedded.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.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener
- deduceMainApplicationClass方法找出main方法所在的类并保存到mainApplicationClass成员变量。
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
run方法
SpringApplication类的run静态方法代码如下所示,可见其调用了上文的构造函数和run成员方法。
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);
}
run成员方法代码如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
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, analyzers, ex);
throw new IllegalStateException(ex);
}
}
run成员方法的主要流程如下:
- 创建SpringApplicationRunListener类型的实例;
- 为应用上下文准备环境;
- 创建应用上下文;
- 准备应用上下文;
- 刷新应用上下文。
创建SpringApplicationRunListener
在创建SpringApplicationRunListener的过程中,首先getRunListeners方法在各META/spring.factories文件查找org.springframework.boot.SpringApplicationRunListener的工厂实现类,然后以该SpringApplication对象和main方法的命令行参数为实参调用这些实现类的构造函数实例化它们,最后各SpringApplicationRunListener的starting回调方法被调用。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
-
SpringApplicationRunListener接口专门用于监听run方法,其代码如下所示,该接口的实现类需要声明一个公有的以SpringApplication和字符串数组为参数的构造函数。
public interface SpringApplicationRunListener { void starting(); void environmentPrepared(ConfigurableEnvironment environment); void contextPrepared(ConfigurableApplicationContext context); void contextLoaded(ConfigurableApplicationContext context); void finished(ConfigurableApplicationContext context, Throwable exception); }
- SpringApplicationRunListeners类是SpringApplicationRunListener的集合,其方法内部都是依次调用各SpringApplicationRunListener同名的回调方法。
class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<SpringApplicationRunListener>(listeners); } public void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } } public void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } } // 省略一些代码 }
- 以spring-boot-1.5.15.RELEASE.jar中的META/spring.factories为例,该文件只包含一个EventPublishingRunListener;
EventPublishingRunListener类的部分代码如下,它的构造函数将SpringApplication的listeners成员变量保存的各ApplicationListener加入事件广播器中,其他回调方法在内部广播了相应的事件给各ApplicationListener。# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } @Override @SuppressWarnings("deprecation") public void starting() { this.initialMulticaster .multicastEvent(new ApplicationStartedEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { } // 省略一些代码
准备环境
准备环境是由prepareEnvironment方法完成的,其代码如下所示。
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
- 首先创建环境,若是Web环境则创建StandardServletEnvironment,否则创建StandardEnvironment;
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webEnvironment) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }
- 然后是配置环境,分别配置了属性源和配置文件;
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); } protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource( name + "-" + args.hashCode(), args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } } protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { environment.getActiveProfiles(); // ensure they are initialized // But these ones should go first (last wins in a property key clash) Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); environment.setActiveProfiles(profiles.toArray(new String[profiles.size()])); }
- 最后各SpringApplicationRunListener的environmentPrepared回调方法被调用。
创建应用上下文
createApplicationContext方法创建上下文,如果applicationContextClass字段指定了上下文类型则创建该类型的应用上下文,否则对Web环境默认创建AnnotationConfigEmbeddedWebApplicationContext,对非Web环境创建AnnotationConfigApplicationContext。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
所创建的应用上下文即是DispatcherServlet的应用上下文,这与传统的Spring MVC工程不同。
准备应用上下文
prepareContext方法用于准备上面创建的应用上下文,其代码如下所示。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
该方法主要做了以下几件事:
- 为创建的应用上下文设置了之前创建的环境;
- postProcessApplicationContext方法做了创建应用上下文后的处理工作,为应用上下文注册了bean名称生成策略,子类可以重写该方法自定义其他工作;
- applyInitializers方法按各ApplicationContextInitializer的添加顺序调用其initialize回调方法,请注意除了上文提到jar包的META/spring.factories文件中可以指定该接口的工厂实现类之外,还可以在调用run方法之前通过SpringApplication类的addInitializers成员方法添加ApplicationContextInitializer;
- 各SpringApplicationRunListener的contextPrepared回调方法被调用;
- load方法将bean定义加载到应用上下文中(还未实例化);
- 各SpringApplicationRunListener的contextLoaded回调方法被调用。
刷新应用上下文
refreshContext方法刷新创建的应用上下文,根据上一步加载的bean定义实例化各单例bean。
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
应用上下文刷新后
应用上下文刷新后run方法调用了afterRefresh方法执行所有的Runner,并调用各SpringApplicationRunListener的finished回调方法。
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
afterRefresh方法代码如下所示:
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
- 首先找出应用上下文中所有的ApplicationRunner和CommandLineRunner,实际使用时这两个接口的实现类用@Component注解修饰即可;
- 然后对这些ApplicationRunner和CommandLineRunner按升序排序,它们的实现类都可以选择地实现Ordered接口或者用@Order注解修饰;
- 最后按顺序调用这些Runner的run回调方法。