以springboot 2.0.2.RELEASE版本为例
1.pom.xml引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
2.查看启动入口类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringdemoApplication {
public static void main(String[] args) {
System.out.println("Service start"); // 自己添加的标记,最开始执行
SpringApplication.run(SpringdemoApplication.class, args);
System.out.println("Service end"); // 自己添加的标记,最后执行
}
}
3.查看@SpringBootApplication注解
// 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Target(ElementType.TYPE)
// 注解的生命周期,保留到class文件中(三个生命周期)
@Retention(RetentionPolicy.RUNTIME)
// 表明这个注解应该被javadoc记录
@Documented
// 子类可以继承该注解
@Inherited
// 继承了Configuration,表示当前是注解类
@SpringBootConfiguration
// 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@EnableAutoConfiguration
// 扫描路径设置(具体使用待确认)
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
4.执行main入口函数,首先会打印之前添加的:”Service start”
5.进入该函数运行:
// 会有两个参数:
// 参数1:初始化一个SpringApplication对象,将入口类作为springboot的初始化参数
// 参数2:args入参,不设置的时候为空:{}
SpringApplication.run(SpringdemoApplication.class, args);
6.进入该run函数,如下:
// (1)、入口run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
// args={},primarySource=SpringdemoApplication.class
// (2)将primarySource封装为数组传递,调用下一个run方法
return run(new Class[]{primarySource}, args);
}
// (3)下一个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
(4)调用SpringApplication的构造函数实例化,实例化后再执行该类的run方法
return (new SpringApplication(primarySources)).run(args);
}
7.查看SpringApplication的构造函数:
public SpringApplication(Class<?>... primarySources) {
// this为调用本类中的另一个构造函数
this(null, primarySources);
}
8.另一个构造函数如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; // 传入为空
// 判断是否有入口类
Assert.notNull(primarySources, "PrimarySources must not be null");
// 转化为元素插入的顺序来维护集合的链接表
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断当前环境是什么环境:Servlet、Reactive、或者不是web环境,判断逻辑是classpath中的类?????? 比如win10下这个值是SERVELET
this.webApplicationType = deduceWebApplicationType();
// 初始化器initializers:这些初始化器(initializers)是Spring Boot通过读取每个jar包下的/META-INF/spring.factories文件中的配置获取的。每一个initailizer都是一个实现了ApplicationContextInitializer接口的实例。ApplicationContextInitializer是Spring IOC(控制反转)容器中提供的一个接口:
//【特别要注意的是,这块的扫描结果,是为了run方法中的prepareContext和refreshContext函数初始化的,参见:https://www.cnblogs.com/hzhuxin/p/7742365.html】
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 监听器listeners:从spring.factories文件中找出key为ApplicationListener的类并实例化后设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器
// 【参见:https://blog.csdn.net/Bob_666/article/details/79715156】
setListeners((Collection)
// 该方法获取执行类型子类实例集合,不太明白????????????????
getSpringFactoriesInstances(ApplicationListener.class));
// 获取当前调用栈,找到入口方法main所在的类,并将其复制给SpringApplication对象的成员变量mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
9.开始执行run方法【具体的run方法,待后续】:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();# 任务时间监听器启动
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 核心点:会打印springboot的启动标志,直到server.port端口启动
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 会调用各种Runners进行操作
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
10.Runners操作:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 这里的时候,runners就会得到各种实现了CommandLineRunner的类
AnnotationAwareOrderComparator.sort(runners); # 会根据order对runner排序
// 依次遍历runner,并将其中的值取出来,执行run方法
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
} # 这里runner实际上是为用户提供了两种开机后启动的程序代码
其它资源参见:http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow
【未完待续,待分析】