注明:以下内容基于spring-boot-1.4.2,starter为spring-boot-starter-web。
继实例化SpringApplication以后,变量的初始化工作已经完成,紧接着就是调用run方法完成整个启动过程。run方法包含的代码行数不多,但是包含的逻辑并不少。
* from org.springframework.boot.SpringApplication
/**
* 运行spring程序,创建并且刷新一个新的ApplicationContext.
* @param 应用参数(通常来自于java的main函数
* @return 一个运行的ApplicationContext
*/
public ConfigurableApplicationContext run(String... args) {
// 步骤1:开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//步骤2:配置Headless
configureHeadlessProperty();
//步骤3:获取活动的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//步骤4:启动监听器
listeners.started();
try {
//步骤5:初始化应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//步骤6:准备运行环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//步骤7:打印启动头
Banner printedBanner = printBanner(environment);
//步骤8:创建应用上下文ApplicationContext
context = createApplicationContext();
//步骤9:注册异常分析器
analyzers = new FailureAnalyzers(context);
//步骤10:初始化ApplicationContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//步骤11:刷新ApplicationContext
refreshContext(context);
//步骤12:后续ApplicationContext操作
afterRefresh(context, applicationArguments);
//步骤13:监听器处理
listeners.finished(context, null);
//步骤14:结束计时并打印信息
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);
}
}
这里涉及到一个重要的类,就是ApplicationContext,按照官方的解释这个是提供应用配置信息的核心接口,在程序运行期间它是只读的,但是如果实现支持的话可以被重新加载。这个类提供的功能包括: 1.访问应用组建的bean工程方法,继承自
org.springframework.beans.factory.ListableBeanFactory;
2.用通用的方式加载文件资源,继承自
org.springframework.core.io.ResourceLoader接口;
3.向注册的监听器发布事件,继承自
ApplicationEventPublisher接口;
4.解析消息,支持国际化,继承自
MessageSource接口;
5.context具备多级继承关系,意味这每个servlet都可以优先使用自己的派生context的功能,然后才是全局的公共的context。
下面就run方法中的每一步进行详细解析:
a. 步骤1:开始计时 这里使用到了计时工具类
org.springframework.util.StopWatch。相比直接使用
System.currentTimeMillis()获取时间戳,使用工具类的方式显得更加优雅,并且也为多次计时和多纬度计时提供了可能。在自己的代码里,也可以尝试使用这个工具类
。
* from org.springframework.util.StopWatch
/**
* 开始一项未命名的任务。需要先调用start方法才能调用stop结束方法。
*/
public void start() throws IllegalStateException {
start("");
}
/**
* 开始一项命名过的任务。需要先调用start方法才能调用stop结束方法。
*/
public void start(String taskName) throws IllegalStateException {
//已经开始了任务,不需要再次调用该方法
if (this.running) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.running = true;
this.currentTaskName = taskName;
//记录当前时间戳
this.startTimeMillis = System.currentTimeMillis();
}
b.步骤2:配置Headless
配置系统变量
java.awt.hea
dless,
在该模式下,系统缺少显示设备、键盘或鼠标,依靠系统的计算能力模拟这些特性。
c.步骤3:获取活动的监听器
和初始化时获取监听器一样,这里使用getSpringFactoriesInstances方法获取SpringApplicationRunListener子类的实例,即活动监听器的实例。
* from org.springframework.boot.SpringApplication
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
同样,需要加载的活动监听器的类名在spring.factories中配置,key使用父类的声明,即
org.springframework.boot.SpringApplicationRunListener。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
在获取EventPublishingRunListener实例后,再通过SpringApplicationRunListeners构造函数,将logger和实例填充到SpringApplicationRunListeners对象中。
d.步骤4:启动监听器 SpringApplicationRunListeners可以看作SpringApplicationRunListener子类实例的集合,统一对实例进行操作,比如start启动时。
* from org.springframework.boot.SpringApplicationRunListeners
public void started() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started();
}
}
在配置中仅包含一项活动监听器,即
EventPublishingRunListener,因此只涉及到一次调用。
* from org.springframework.boot.context.event.EventPublishingRunListener
//构造函数,在这里将SpringApplication中初始化过的多个ApplicationListener进行注册
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);
}
}
//广播事件
public void started() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
* from org.springframework.context.event.SimpleApplicationEventMulticaster
//广播事件的实现,向所有注册监听器广播事件,而这个时间就是传入的ApplicationStartedEvent。如果监听器未监听这个事件,那可以直接忽略。
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//如果指定executor,可以使用单独线程来触发时间,通知监听器
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
这是很典型的java
监听器模式,也是观察者模式的另一种形式。每一项监听器处理的内容将在后续文章中进一步展开。到此,初始化过程中加载的监听器也派上用场了。并不是所有监听器都会处理启动事件,在各自的事件处理方法中都各有选择。
e.步骤5:初始化应用参数
这里就是将main函数带入的参数填充到
org.springframework.boot.
DefaultApplicationArguments中。
* from org.springframework.boot.DefaultApplicationArguments
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
f.步骤6:准备运行环境
* from org.springframework.boot.SpringApplication
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建运行环境,因初始化过程中确认了是web工程,即webEnvironment为true,因此此处创建的是StandardServletEnvironment实例。
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置运行环境,包括main函数带入的配置项和项目配置文件带入的配置项
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 广播ApplicationEnvironmentPreparedEvent事件,通知注册的监听器做出相应处理
listeners.environmentPrepared(environment);
//再次确认满足web环境的要求,lib中包含了org.springframework.web.context.ConfigurableWebEnvironment。
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
// 环境准备结束
return environment;
}
g.步骤7:打印启动头
默认打印标识性头图等信息,当然如果你想个性化,也可以做到,可以自定义image头,也可以定义text头。
* from org.springframework.boot.SpringBootBanner
private static final String[] BANNER = { "",
" . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\",
"( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )",
" ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
h.步骤8:创建应用上下文ApplicationContext
web应用和非web应用分别有默认的实现类,web对应
org.springframework.
boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext,非web对应
javax.servlet.Servlet
.springframework.web.context.ConfigurableWebApplicationContext.
* from org.springframework.boot.SpringApplication
/**
* 用于创建ApplicationContext的策略方法。优先考虑显示指定的上下文类,如果没有指定,则使用默认的。
* @return 应用上下文 (还没有刷新)
*/
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);
}
i.步骤9:注册异常分析器
异常分析器的加载和初始化器、注册监听器相同,都是在spring.factories文件中配置,通过反射机制实例化并加载进来。
FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
Assert.notNull(context, "Context must not be null");
this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader);
//加载分析器
this.analyzers = loadFailureAnalyzers(this.classLoader);
//将context中的属性注入到分析器中
prepareFailureAnalyzers(this.analyzers, context);
}
private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
// 获取配置文件中的配置项,key是org.springframework.boot.diagnostics.FailureAnalyzer
List<String> analyzerNames = SpringFactoriesLoader
.loadFactoryNames(FailureAnalyzer.class, classLoader);
List<FailureAnalyzer> analyzers = new ArrayList<FailureAnalyzer>();
for (String analyzerName : analyzerNames) {
try {
// 实例化
Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader)
.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
analyzers.add((FailureAnalyzer) constructor.newInstance());
}
catch (Throwable ex) {
log.trace("Failed to load " + analyzerName, ex);
}
}
// 排序
AnnotationAwareOrderComparator.sort(analyzers);
return analyzers;
}
j.步骤10:初始化ApplicationContext
后续步骤10-步骤12都是对应用上下文进行操作,从准备,刷新到后续处理,过程很长,需要详细描述。
k.步骤13:监听器处理
结束监听器监听
l.步骤14:结束计时并打印信息
计时结束并打印时间信息。