前言

  • 要研究Spring Boot 就需要下载它的源码,传送门:https://github.com/spring-projects/spring-boot

导入Spring Boot 源码

  • 这里是选用了spring-boot-1.5.6.RELEASE版本与之前的spring-framework-4.3.10.RELEASE是相对应的,下面是目录结构
  • 下载后执行clean install -Dmaven.test.skip=true命令,然而并不是那么顺利,直接运行会报错,和spring-framework-4.3.10.RELEASE的源码的编译一样还是要改文件才能正常编译

    • 注释下面的依赖,不然会报找不到的错

      1
      2
      3
      4
      5
      6
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <type>test-jar</type>
      <scope>test</scope>
      </dependency>
    • 注释根pom不必要的模块,不然报错

      1
      2
      <module>spring-boot-test-autoconfigure</module>
      <module>spring-boot-devtools</module>
    • 注释根pom不必要的插件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-checkstyle-plugin</artifactId>
      <version>2.17</version>
      <dependencies>
      <dependency>
      <groupId>com.puppycrawl.tools</groupId>
      <artifactId>checkstyle</artifactId>
      <version>7.1.1</version>
      </dependency>
      </dependencies>
      </plugin>
    • 注释spring-boot-tools模块下的pom.xml的模块,不然报错

      1
      2
      <module>spring-boot-gradle-plugin</module>
      <module>spring-boot-maven-plugin</module>
    • 将一些报错模块测试包Mark Directory as -> Excluded,也可以直接干掉

  • 编译通过之后就可以在本地调spring-boot-samples目录下的示例代码了

    解析

  • 查看Spring Boot的示例代码

    • 使用 @SpringBootApplication 注解,标明是 Spring Boot 应用。通过它,可以开启自动配置的功能。
    • 调用 SpringApplication#run(Class<?>... primarySources) 方法,启动 Spring Boot 应用。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      @SpringBootApplication
      public class SampleTomcatJspApplication extends SpringBootServletInitializer {

      @Override
      protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
      return application.sources(SampleTomcatJspApplication.class);
      }

      public static void main(String[] args) throws Exception {
      SpringApplication.run(SampleTomcatJspApplication.class, args);
      }
      }

SpringApplication

  • 跟进SpringApplication.run(SampleTomcatJspApplication.class, args)方法,可以看到是调用了SpringApplication的静态方法,最终是创建 SpringApplication 对象并执行public ConfigurableApplicationContext run(String... args)方法
1
2
3
4
5
6
7
8
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
// 创建 SpringApplication 对象,并执行运行。
return new SpringApplication(sources).run(args);
}
  • 进入org.springframework.boot.SpringApplication#run(java.lang.String...)方法,可以看到主要逻辑也是构造ApplicationContext容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* 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) {
// 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
// 配置 headless 属性,这个逻辑,可以无视,和 AWT 相关。
configureHeadlessProperty();
// 获得 SpringApplicationRunListener 的数组,并启动监听
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 创建 ApplicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 打印 Spring Banner
Banner printedBanner = printBanner(environment);
// 创建 Spring 容器。
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
// 主要是调用所有初始化类的 initialize 方法
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 初始化 Spring 容器。
refreshContext(context);
// 执行 Spring 容器的初始化的后置逻辑
afterRefresh(context, applicationArguments);
// 通知 SpringApplicationRunListener 的数组,Spring 容器启动完成
listeners.finished(context, null);
// 停止 StopWatch 统计时长
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
  • Banner printedBanner = printBanner(environment);用于打印Spring,也可以自己添加banner.txt修改打印

    1
    2
    3
    4
    5
    6
    7
      .   ____          _            __ _ _
    /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
    \\/ ___)| |_)| | | | | || (_| | ) ) ) )
    ' |____| .__|_| |_|_| |_\__, | / / / /
    =========|_|==============|___/=/_/_/_/
    :: Spring Boot ::
  • 进入printBanner(environment)方法可以看到是构造了一个SpringApplicationBannerPrinter对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
// 构造SpringApplicationBannerPrinter
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
// 打印在log file中
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
  • 查看SpringApplicationBannerPrinter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SpringApplicationBannerPrinter {
// 配置该属性用于配置自定义banner的位置
static final String BANNER_LOCATION_PROPERTY = "banner.location";
// 图片BANNER_IMAGE?
static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
// 默认banner的存放位置
static final String DEFAULT_BANNER_LOCATION = "banner.txt";

static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
// 默认打印处理类
private static final Banner DEFAULT_BANNER = new SpringBootBanner();

private final ResourceLoader resourceLoader;

private final Banner fallbackBanner;

SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
this.resourceLoader = resourceLoader;
this.fallbackBanner = fallbackBanner;
}

...
  • SpringBootBanner默认打印处理类,可以看到是直接拼接了一个字符数组然后for
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* Default Banner implementation which writes the 'Spring' banner.
*
* @author Phillip Webb
*/
class SpringBootBanner implements Banner {

private static final String[] BANNER = { "",
" . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\",
"( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )",
" ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };

private static final String SPRING_BOOT = " :: Spring Boot :: ";

private static final int STRAP_LINE_SIZE = 42;

@Override
public void printBanner(Environment environment, Class<?> sourceClass,
PrintStream printStream) {
// 循环打印
for (String line : BANNER) {
printStream.println(line);
}
String version = SpringBootVersion.getVersion();
version = (version == null ? "" : " (v" + version + ")");
String padding = "";
while (padding.length() < STRAP_LINE_SIZE
- (version.length() + SPRING_BOOT.length())) {
padding += " ";
}

printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT,
AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version));
printStream.println();
}

}

Create ApplicationContext

  • context = createApplicationContext();用于创建ApplicationContext,不同环境会创建不同的
    • 非web环境构造AnnotationConfigApplicationContext
    • web环境构造AnnotationConfigEmbeddedWebApplicationContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 非web环境构造的application context
* 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";

/**
* web环境构造的application context
* 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";

protected ConfigurableApplicationContext createApplicationContext() {
// 根据 webApplicationType 类型,获得 ApplicationContext 类型
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);
}
}
// 创建 ApplicationContext 对象
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

Refresh ApplicationContext

  • refreshContext(context);用于初始化 Spring 容器,可以看到主要逻辑也是调用ApplicationContextrefresh()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void refreshContext(ConfigurableApplicationContext context) {
// 开启(刷新)Spring 容器
refresh(context);
// 注册 ShutdownHook 钩子
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();
}

Call Runners

  • afterRefresh(context, applicationArguments);用于执行 Spring 容器的初始化后的后置逻辑,可以看到是执行了ApplicationRunnerCommandLineRunner接口的run方法,如果你想在Spring Boot容器构造完成之后额外做一些事情,可以实现这两个接口来定义

    • ApplicationRunnerrun方法的参数为ApplicationArguments

      • 示例代码:
        1
        2
        3
        4
        5
        6
        7
        8
        @Component
        @Order(value = 10)
        public class AgentApplicationRun2 implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments applicationArguments) throws Exception {

        }
        }
    • CommandLineRunner接口中run方法的参数为String数组。想要更详细地获取命令行参数,那就使用ApplicationRunner接口

      • 示例代码
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
                @Component
        @Order(value = 11)
        public class AgentApplicationRun implements CommandLineRunner {

        @Override
        public void run(String... strings) throws Exception {

        }
        }
        ```
        * `ApplicationRunner `
        ```java
        protected void afterRefresh(ConfigurableApplicationContext context,
        ApplicationArguments args) {
        callRunners(context, args);
        }

        private void callRunners(ApplicationContext context, ApplicationArguments args) {
        // 获得所有 Runner
        List<Object> runners = new ArrayList<Object>();
        // 获得所有 ApplicationRunner Bean
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        // 获得所有 CommandLineRunner Bean
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        // 排序 runners
        AnnotationAwareOrderComparator.sort(runners);
        // 遍历 Runner 数组,执行逻辑
        for (Object runner : new LinkedHashSet<Object>(runners)) {
        if (runner instanceof ApplicationRunner) {
        callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
        callRunner((CommandLineRunner) runner, args);
        }
        }
        }

总结

参考