http://blog.csdn.net/u011179993/article/details/51475732


一、 快速创建一个Boot应用

使用maven

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.5.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

应用程序结构

App.Java作为启动类

@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.jazz.controller"})
@Configuration
public class App {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(App.class);
        application.run(args);
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

* 第一个controller *

@RestController
public class FirstController {

    @RequestMapping("/test_1.service")
    public String queryTeacher() {
        String a="Hello World";
        return a;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

二、SpringApplication类概要

从上节可以看出:SpringApplication用来启动整个应用程序。

主要功能

1.根据classspath创建合适的ApplicationContext 
2.注册CommandLinePropertySource生成命令行参数 
3.刷新application context,载入所有bean 
4.运行CommandLineRunner bean

使用方式

public static void main(String[] args) throws Exception {
    //实例化--使用当前类作为source(带有@Configuration的配置类)
    SpringApplication app = new SpringApplication(MyApplication.class);
    // 启动应用
    app.run(args)
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

当然还有其他方式,不过类的内部都是根据这种方式转变而来。

配置(source)

Boot官方文档建议使用带有@Configuration的java类作为配置,而不是XML或其他配置方式,本系列文章都使用@Configuration。

三、SpringApplication 实例化

通过实例化 SpringApplication application = new SpringApplication(App.class); 
主要调用了SpringApplication内部的initialize(..)方法,源码如下:

private void initialize(Object[] sources) {
    //1
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    //2
    this.webEnvironment = deduceWebEnvironment(); 
    //3
    setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class)); 
    //4 
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 
    //5
    this.mainApplicationClass = deduceMainApplicationClass();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

流程分析 
1.设置配置类

2.推断环境类型(标准或web)

3.实例化spring.factory文件中ApplicationContextInitializer对应的类

4.实例化spring.factory文件中ApplicationListener对应的类

5.推断主类


四、SpringApplication 执行

SpringBoot#run(String… args)

public ConfigurableApplicationContext run(String... args) {
    //计时器,记录应用启动消耗时间
    StopWatch stopWatch = new StopWatch();
    ConfigurableApplicationContext context = null;

    //用来设置java.awt.headless 属性是true 还是false,是J2SE的一种模式用于在缺少显示屏、键盘 
    //或者鼠标时的系统配置
    configureHeadlessProperty(); 

    //实例化Boot的SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args); 

    //执行所有SpringApplicationRunListener的start方法
    listeners.started(); 

    try {
        ApplicationArguments applicationArguments 
                       = new DefaultApplicationArguments( args);

        //5创建并刷新Spring上下文 
        context = createAndRefreshContext(listeners, applicationArguments);

        //6执行刷新完上下文的流程
        afterRefresh(context, applicationArguments);

        //7执行所有SpringApplicationRunListener的finish方法。
        listeners.finished(context, null);

        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, ex);
        throw new IllegalStateException(ex);
    }
}
  • 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
  • 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

createAndRefreshContext(..)方法

private ConfigurableApplicationContext createAndRefreshContext(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
    ConfigurableApplicationContext context;

    // 5.1 创建并配置环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    if (isWebEnvironment(environment) && !this.webEnvironment) {
        environment = convertToStandardEnvironment(environment);
    }

    if (this.bannerMode != Banner.Mode.OFF) {
        printBanner(environment);
    }

    //5.2 Create, load, refresh 和run ApplicationContext
    context = createApplicationContext();
    context.setEnvironment(environment); 

    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    //5.3 添加boot 特殊bean
    context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);

    //5.4 载入 sources
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[sources.size()]));
    listeners.contextLoaded(context);

    //5.5 刷新 context
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
    return context;
}
  • 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
  • 50
  • 51
  • 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
  • 50
  • 51

综上两个方法,进行流程分析: 
1.程序启动*** 
Boot程序开始启动,执行其开始***。这个方法真正会广播ApplicationStartedEvent事件给ApplicationListener。 
执行以下***

【LoggingApplicationListener】 
配置日志

【ClasspathLoggingApplicationListener】 
debug模式则开始日志信息

【LiquibaseServiceLocatorApplicationListener】 
存在Liquibase类则配置Liquibase

注意:有些***监听了多个事件,所以下文中还会出现。

2.创建并配置环境 
根据命令行参数及其系统属性、系统环境、Servlet参数等创建环境,并配置profile等。 
配置完成的环境对象如下图: 

3.执行环境准备好*** 
广播ApplicationEnvironmentPreparedEvent事件。 这个事件可以用来修改环境中的属性源。

【ConfigFileApplicationListener】 
这个类会加载spring.factory文件中的EnvironmentPostProcessor实例到list中,~*这是一个SpringBoot的拓展点。* 
然后添加本身,因为它也是EnvironmentPostProcessor 
然后依次执行这这些EnvironmentPostProcessor的postProcessEnvironment方法。如下 
—SpringApplicationJsonEnvironmentPostProcessor- 
把环境中spring.application.json的json值转化为MapPropertySource,并将此MapPropertySource添加到环境的属性源列表中 
如果在程序启动之前添加了一个系统属性json

public static void main(String[] args) {
    System.getProperties().put("spring.application.json", "{ \"name\": \"Web\"}");
    SpringApplication application = new SpringApplication(App.class);  
    application.run(args);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

那么,此processor运行完之后,环境的属性源为: 

—CloudFoundryVcapEnvironmentPostProcessor- 
对CloudFoundry提供支持 
—ConfigFileApplicationListener- 
首先,向环境的属性源列表中添加一个Random属性源,如下 

然后,从配置文件中提取相应的键值添加到名为applicationConfigurationProperties属性源中,这个属性源可以包含多个子属性源 

【AnsiOutputApplicationListener】 
解析环境中以下属性 
spring.output.ansi.enabled 来决定是否颜色输出模式,值为枚举AnsiOutput.Enabled (DETECT, ALWAYS, NEVER) 
spring.output.ansi.console-available (?测试未成功)是否控制台输出

【LoggingApplicationListener】 
(?未测试)配置日志相关,输出配置相关信息 

【BackgroundPreinitializer】 
初始化一些信息

runSafely(new MessageConverterInitializer());
runSafely(new MBeanFactoryInitializer());
runSafely(new ValidationInitializer());
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

【DelegatingApplicationListener】

这个***监听所有事件,如果是ApplicationEnvironmentPreparedEvent,那么获取环境中key为context.listener.classes的***,后续对这些***广播相应的事件~拓展点
  • 1
  • 1

【FileEncodingApplicationListener】

对spring.mandatoryFileEncoding进行处理
  • 1
  • 1

4.打印Banner图形

感兴趣的同学可以实现Banner定制自己的Banner 
或者指定banner.location,或者直接添加banner.txt。 
~这里还是比较有趣的、^_^

5.创建ApplicationContext并添加上面的环境 
如果没有在实例变量中添加ApplicationContext,那么根据是否是网络环境来决定创建添加以下两种ApplicationContext:

AnnotationConfigEmbeddedWebApplicationContext
AnnotationConfigApplicationContext。
  • 1
  • 2
  • 1
  • 2

然后把环境设置给ApplicationContext

6.后处理 ApplicationContext 
根据是否含有某些实例变量来向ApplicationContext添加:

beanNameGenerator
resourceLoader
resourceLoader
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

7.执行ApplicationContextInitializer 
初始化时添加到实例变量的ApplicationContextInitializer,依次执行: 
 
【DelegatingApplicationContextInitializer】 
这个类取得环境中“context.initializer.classes”对应的多个ApplicationContextInitializer,用“,”分割,并依次执行~这里可以进行自己拓展,来修改ApplicationContext

【ContextIdApplicationContextInitializer】 
根据 
${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}} 
来设置applicationContext的ID

【ConfigurationWarningsApplicationContextInitializer】 
为applicationContext添加一个BeanDefinitionRegistryPostProcessor:ConfigurationWarningsPostProcessor,来输出警告信息 
例如@ComponentScan没有写明具体扫描的包名称

【ServerPortInfoApplicationContextInitializer】 
添加***监听EmbeddedServletContainerInitializedEvent事件,用来设置嵌入的servlet容器端口号(?) 
【AutoConfigurationReportLoggingInitializer】 
向ApplicationContext添加AutoConfigurationReportListener用来输出自动配置报告

8.listeners.contextPrepared(context); 
把之前用到的广播器添加到ApplicationContext中,没有发布任何事件

9输出Profile日志

10注册命令行参数bean 
向ApplicationContext注册name为springApplicationArguments的Bean

11 载入配置sources 
使用ApplicationContext.load载入资源

12 发布给ApplicationContext***ApplicationPreparedEvent 
执行contextLoaded 
【ConfigFileApplicationListener】 
【LoggingApplicationListener】 
【DelegatingApplicationListener】

13 refresh(context); 
刷新上下文,如果是网络环境,那么进入 
AnnotationConfigEmbeddedWebApplicationContext的世界

14 运行完成 
主要是运行实现ApplicationRunner和CommandLineRunner接口的类

15 程序完成*** 
【ConfigFileApplicationListener】 
【DelegatingApplicationListener】


后面我们将会从这12步分析~深入理解SpringBoot和Spring的各种原理。 
我们发现spring的***贯穿在始终,所以下一节我们将会从***入手

<dl class="digg digg&#95;disable" id="btnDigg" style="display&#58;inline&#45;block&#59;width&#58;72px&#59;overflow&#58;hidden&#59;background&#58;rgb&#40;153&#44;153&#44;153&#41;&#59;color&#58;rgb&#40;255&#44;255&#44;255&#41;&#59;font&#45;family&#58;Arial&#44; Console&#44; Verdana&#44; &#39;Courier New&#39;&#59;"> <dt style="font&#45;size&#58;27px&#59;line&#45;height&#58;30px&#59;font&#45;family&#58;&#39;Microsoft YaHei&#39;&#59;"> 顶 </dt> <dd style="line&#45;height&#58;22px&#59;font&#45;family&#58;Arial&#59;"> 1 </dd> </dl>