本文采用的是springboot 1.5.9版本.
重新回顾一下run()方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//1. 初始化stopwatch,用于计时,本文不详细讲解
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//2. 设置系统属性java.awt.handless为true,
//该属性表示系统可以在缺少显示设备、键盘或鼠标这些外设的情况下进行使用,本文不详细讲解。
configureHeadlessProperty();
//3. 获得runListeners,并启动(调用listener的onApplicationEvent方法)
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//4. 创建DefaultApplicationArguments(该类含有args),然后调用prepareEnvironment()
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//5. 打印banner
Banner printedBanner = printBanner(environment);
//6. 创建Springboot的上下文ApplicationContext
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//7. prepareContext
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//8. spring最出名的refresh()方法
refreshContext(context);
//9. TODO
afterRefresh(context, applicationArguments);
//10. TODO
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
//11. 处理异常
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
打印banner相对整个run方法而言没有那么重要,因此只进行一些简单的介绍。
首先,对banner接口进行介绍:
public interface Banner {
void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);
enum Mode {
//关闭banner
OFF,
//控制台
CONSOLE,
//日志
LOG
}
}
Banner接口提供了一个打印banner的方法,然后枚举Mode提供了三种可能的选择方式。Banner接口有以下几个主要的实现类:
接下来介绍printBanner()方法:
private Banner printBanner(ConfigurableEnvironment environment) {
//如果在启动类中设置Banner.Mode.OFF的话,则禁用banner
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
//这里resourceLoader为空,因此会创建一个DefaultResourceLoader
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
//LOG的话,则输出到log中
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
//输出到控制台(默认情况)
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
print是按照ImageBanner->ResourceBanner的顺序来进行打印的,具体代码这里便不深入介绍。
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//1. 因为webEnvironment不为空,所以获取的是web的默认context类
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);
}
}
//2. 调用BeanUtils.instantiate创建context类
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
BeanUtils.instantiate(class)实际上就是调用class.newInstance(),无参构造器创建实例:
public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException {
//忽略判断代码
try {
return clazz.newInstance();
}
catch (InstantiationException ex) {
...
}
}
因此接下来需要去看AnnotationConfigEmbeddedWebApplicationContext类:
public AnnotationConfigEmbeddedWebApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
可以看到,该类中实际上包含了一个reader和一个scanner,看到这两个类我立即反应过来这个类实际上就是spring中的AnnotationConfigApplicationContext,如果对该类不了解,可以阅读我的另一篇文章:spring 源码系列(四)- AnnotationConfigApplicationContext
TODO