【第三十九讲】Boot 启动流程

【第三十九讲】Boot 启动流程

  1. SpringApplication 构造分析
  2. SpringApplication run 分析

12大步骤、7大事件

阶段一:springApplication

1.来源演示

当作外部处理

添加

@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
 return new TomcatServletWebServerFactory();
}

既可
在这里插入图片描述

输出结果

在这里插入图片描述

来源于xml

public static void main(String[] args) {
    System.out.println("1.演示获取 Bean Definition 源");
    SpringApplication springApplication = new SpringApplication(A39_1.class);
    Set<String> set = new HashSet<>();
    set.add("classpath:b01.xml");
    springApplication.setSources(set);
    
    ConfigurableApplicationContext context = springApplication.run(args);
    for (String name : context.getBeanDefinitionNames()) {
        // 获取来源
        System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
    }

    context.close();
}

在这里插入图片描述
在这里插入图片描述

2.推断应用类型

在这里插入图片描述

因为加入的为web依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

推断出类型为 应用类型SERVLET

在这里插入图片描述

3.初始化器

System.out.println("3.演示 ApplicationContext 初始化器");
springApplication.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        if(applicationContext instanceof GenericApplicationContext ){
            GenericApplicationContext context = (GenericApplicationContext) applicationContext;
            context.registerBean("bean3",Bean3.class);
        }
    }
});

在这里插入图片描述

4. 监听事件

System.out.println("4.演示监听器与事件");
springApplication.addListeners(new ApplicationListener<ApplicationEvent>() {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("\t事件为" + event.getClass());
    }
});

5.主类推断

在这里插入图片描述

System.out.println("5.演示主类推断");
Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
deduceMainApplicationClass.setAccessible(true);
System.out.println("\t 5-------" + deduceMainApplicationClass.invoke(springApplication));

在这里插入图片描述

阶段二:执行 run 方法

public class A39_3 {
    public static void main(String[] args) throws Exception{
        SpringApplication app = new SpringApplication();
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                System.out.println("执行初始化器增强...");
            }
        });

        System.out.println("----------2.封装启动 args");
            DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
        System.out.println("----------8.创建容器");
            GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

        System.out.println("----------9.准备容器");
            for (ApplicationContextInitializer initializer : app.getInitializers()) {
                initializer.initialize(context);
            }
        System.out.println("----------10.加载 bean 定义");
            DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
            //1.配置类
            AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
            reader1.register(Config.class);
            //2.xml
            XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
            reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
            //3.包扫描
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
            scanner.scan("com.example.spring01.com.a39.sub");
        System.out.println("----------11.refresh 容器");
            context.refresh();

            for (String name : context.getBeanDefinitionNames()) {
                System.out.println("name:"+ name + "来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());

            }
        System.out.println("----------12.执行 runner");
            for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
                runner.run(args);
            }

            for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
                runner.run(arguments);
            }
    }

1.得到 SpringApplicationRunListeners

名字取得不好,实际是事件发布器

  • 发布 application starting 事件

学到了什么
a. 如何读取 spring.factories 中的配置
b. run 方法内获取事件发布器 (得到 SpringApplicationRunListeners) 的过程, 对应步骤中
1.获取事件发布器
发布 application starting 事件1️⃣
发布 application environment 已准备事件2️⃣
发布 application context 已初始化事件3️⃣
发布 application prepared 事件4️⃣
发布 application started 事件5️⃣
发布 application ready 事件6️⃣
这其中有异常,发布 application failed 事件7️⃣

public class A39_2 {
    public static void main(String[] args) throws Exception {

        // 添加 app 监听
        SpringApplication app = new SpringApplication();
        app.addListeners( event -> System.out.println(event.getClass()));

        // 获取事件发送器实现类名
        List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());
        for (String name : names) {
            System.out.println("name---"  +  name);
            // 获取事件发布器的类
            Class<?> aClass = Class.forName(name);
            // 构造器
            Constructor<?> constructor = aClass.getConstructor(SpringApplication.class, String[].class);
            // 创建对象
            SpringApplicationRunListener publisher = (SpringApplicationRunListener) constructor.newInstance(app, args);
            // 参数
            GenericApplicationContext context = new GenericApplicationContext();

            //发布事件
            publisher.starting(); // Spring boot 开始启功
            publisher.environmentPrepared(new StandardEnvironment()); // 环境信息准备完毕
            publisher.contextPrepared(context); // 在spring 容器创建,并调用初始化器之后,发送此事件
            publisher.contextLoaded(context); // 所有 bean definition 加载完毕
            context.refresh();
            publisher.started(context); // spring 容器初始化完成(refresh 方法调用完毕)
            publisher.running(context); // spring boot 启动完毕

            publisher.failed(context,new Exception("出错了")); //spring boot 启动出错
        }
    }
}

在这里插入图片描述

2.封装启动 args

DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

8.创建容器

  • 发布 application context 已初始化事件
GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

--------
 private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
        GenericApplicationContext context = null;
        switch (type) {
            case SERVLET:
                context =  new AnnotationConfigServletWebServerApplicationContext();
                return context;
            case REACTIVE:
                context =  new AnnotationConfigReactiveWebServerApplicationContext();
                return context;
            case NONE:
                context = new AnnotationConfigApplicationContext();
                break;
        }
        return context;
    }
    

9.准备容器

for (ApplicationContextInitializer initializer : app.getInitializers()) {
                initializer.initialize(context);
            }

10.加载 bean 定义

  • 发布 application prepared 事件

三种方式

  1. Config配置类
  2. xml 文件
  3. 包扫描
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
//1.
AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
reader1.register(Config.class);
//2.
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
//3.
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.example.spring01.com.a39.sub");
@Configuration
    static class Config {
        @Bean
        public Bean5 bean5() {
            return new Bean5();
        }

        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public CommandLineRunner commandLineRunner() {
            return new CommandLineRunner() {
                @Override
                public void run(String... args) throws Exception {
                    System.out.println("commandLineRunner()..." + Arrays.toString(args));
                }
            };
        }

        @Bean
        public ApplicationRunner applicationRunner() {
            return new ApplicationRunner() {
                @Override
                public void run(ApplicationArguments args) throws Exception {
                    System.out.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
                    System.out.println(args.getOptionNames());
                    System.out.println(args.getOptionValues("server.port"));
                    System.out.println(args.getNonOptionArgs());
                }
            };
        }
    }

b03.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bean4" class="com.example.spring01.com.a39.A39_3.Bean4"></bean>
</beans>

在这里插入图片描述

11. refresh 容器

* 发布 application started 事件

12执行 runner

* 发布 application ready 事件

* 这其中有异常,发布 application failed 事件

在config配置中加入

两个接口的参数不同

@Bean
public CommandLineRunner commandLineRunner() {
    return new CommandLineRunner() {
        @Override
        public void run(String... args) throws Exception {
            System.out.println("commandLineRunner()..." + Arrays.toString(args));
        }
    };
}

@Bean
public ApplicationRunner applicationRunner() {
    return new ApplicationRunner() {
        @Override
        public void run(ApplicationArguments args) throws Exception {
            System.out.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
            System.out.println(args.getOptionNames());
            System.out.println(args.getOptionValues("server.port"));
            System.out.println(args.getNonOptionArgs());
        }
    };
}

参数

  1. –server.port=8080 debug
    在这里插入图片描述

runner 执行

for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
    runner.run(args);
}

for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
    runner.run(arguments);
}

上面阶段

org.springframework.boot.Step3

public class Step3 {
    public static void main(String[] args) throws IOException {
        ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
        env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("step3.properties")));
        env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
//        System.out.println(env.getProperty("JAVA_HOME"));

        System.out.println(env.getProperty("server.port"));
    }
}

先从系统属性里面找

在这里插入图片描述

Step4

ConfigurationPropertySources——名字不一致进行统一
在这里插入图片描述

public class Step4 {

    public static void main(String[] args) throws IOException, NoSuchFieldException {
        ApplicationEnvironment env = new ApplicationEnvironment();
        env.getPropertySources().addLast(
            new ResourcePropertySource("step4", new ClassPathResource("step4.properties"))
        );
        // 加入后可以读取
        ConfigurationPropertySources.attach(env);
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }

        System.out.println(env.getProperty("user.first-name"));
        System.out.println(env.getProperty("user.middle-name"));
        System.out.println(env.getProperty("user.last-name"));
    }
}

Step5

/*
    可以添加参数 --spring.application.json={\"server\":{\"port\":9090}} 测试 SpringApplicationJsonEnvironmentPostProcessor
 */
public class Step5 {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication();
        app.addListeners(new EnvironmentPostProcessorApplicationListener());

        /*List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Step5.class.getClassLoader());
        for (String name : names) {
            System.out.println(name);
        }*/

        EventPublishingRunListener publisher = new EventPublishingRunListener(app, args);
        ApplicationEnvironment env = new ApplicationEnvironment();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        publisher.environmentPrepared(new DefaultBootstrapContext(), env);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }

    }

    private static void test1() {
        SpringApplication app = new SpringApplication();
        ApplicationEnvironment env = new ApplicationEnvironment();

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        ConfigDataEnvironmentPostProcessor postProcessor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
        postProcessor1.postProcessEnvironment(env, app);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        RandomValuePropertySourceEnvironmentPostProcessor postProcessor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
        postProcessor2.postProcessEnvironment(env, app);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        System.out.println(env.getProperty("server.port"));
        System.out.println(env.getProperty("random.int"));
        System.out.println(env.getProperty("random.int"));
        System.out.println(env.getProperty("random.int"));
        System.out.println(env.getProperty("random.uuid"));
        System.out.println(env.getProperty("random.uuid"));
        System.out.println(env.getProperty("random.uuid"));
    }
}

Step6

public class Step6 {
    // 绑定 spring.main 前缀的 key value 至 SpringApplication, 请通过 debug 查看
    public static void main(String[] args) throws IOException {
        SpringApplication application = new SpringApplication();
        ApplicationEnvironment env = new ApplicationEnvironment();
        env.getPropertySources().addLast(new ResourcePropertySource("step4", new ClassPathResource("step4.properties")));
        env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));

//        User user = Binder.get(env).bind("user", User.class).get();
//        System.out.println(user);

//        User user = new User();
//        Binder.get(env).bind("user", Bindable.ofInstance(user));
//        System.out.println(user);

        System.out.println(application);
        Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
        System.out.println(application);
    }

    static class User {
        private String firstName;
        private String middleName;
        private String lastName;
        public String getFirstName() {
            return firstName;
        }
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
        public String getMiddleName() {
            return middleName;
        }
        public void setMiddleName(String middleName) {
            this.middleName = middleName;
        }
        public String getLastName() {
            return lastName;
        }
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
        @Override
        public String toString() {
            return "User{" +
                   "firstName='" + firstName + '\'' +
                   ", middleName='" + middleName + '\'' +
                   ", lastName='" + lastName + '\'' +
                   '}';
        }
    }
}

Step7

public class Step7 {
    public static void main(String[] args) {
        ApplicationEnvironment env = new ApplicationEnvironment();
        SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(
                new DefaultResourceLoader(),
                new SpringBootBanner()
        );
        // 测试文字 banner
//        env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.location","banner1.txt")));
        // 测试图片 banner
//        env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.image.location","banner2.png")));
        // 版本号的获取
        System.out.println(SpringBootVersion.getVersion());
        printer.print(env, Step7.class, System.out);
    }
}

总结

SpringApplication.class

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    // 1.创建事件发布器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();

    Collection exceptionReporters;
    try {
        // 2.封装参数 -- 选项参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //3-6
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //7.Step7
        Banner printedBanner = this.printBanner(environment);
        //8.创建容器
        context = this.createApplicationContext();
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        // 9-10
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 11
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }

        listeners.started(context);
        // 12
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    // 3.Step3    
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        // 4.Step4
        ConfigurationPropertySources.attach((Environment)environment);
    	// 5.Step5
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
    	// 6.Step6
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        // 9.
        this.applyInitializers(context);
    	// 发布事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
		// 10. 加载各种bean
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

阶段一:SpringApplication 构造

  1. 记录 BeanDefinition 源
  2. 推断应用类型
  3. 记录 ApplicationContext 初始化器
  4. 记录监听器
  5. 推断主启动类

阶段二:执行 run 方法

  1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器

    • 发布 application starting 事件1️⃣
  2. 封装启动 args

  3. 准备 Environment 添加命令行参数(*)

  4. ConfigurationPropertySources 处理(*)

    • 发布 application environment 已准备事件2️⃣
  5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

    • application.properties,由 StandardConfigDataLocationResolver 解析
    • spring.application.json
  6. 绑定 spring.main 到 SpringApplication 对象(*)

  7. 打印 banner(*)

  8. 创建容器

  9. 准备容器

    • 发布 application context 已初始化事件3️⃣
  10. 加载 bean 定义

    • 发布 application prepared 事件4️⃣
  11. refresh 容器

    • 发布 application started 事件5️⃣
  12. 执行 runner

    • 发布 application ready 事件6️⃣

    • 这其中有异常,发布 application failed 事件7️⃣

带 * 的有独立的示例

演示 - 启动过程

com.itheima.a39.A39_1 对应 SpringApplication 构造

com.itheima.a39.A39_2 对应第1步,并演示 7 个事件

com.itheima.a39.A39_3 对应第2、8到12步

org.springframework.boot.Step3

org.springframework.boot.Step4

org.springframework.boot.Step5

org.springframework.boot.Step6

org.springframework.boot.Step7

收获💡
  1. SpringApplication 构造方法中所做的操作
    • 可以有多种源用来加载 bean 定义
    • 应用类型推断
    • 添加容器初始化器
    • 添加监听器
    • 演示主类推断
  2. 如何读取 spring.factories 中的配置
  3. 从配置中获取重要的事件发布器:SpringApplicationRunListeners
  4. 容器的创建、初始化器增强、加载 bean 定义等
  5. CommandLineRunner、ApplicationRunner 的作用
  6. 环境对象
    1. 命令行 PropertySource
    2. ConfigurationPropertySources 规范环境键名称
    3. EnvironmentPostProcessor 后处理增强
      • 由 EventPublishingRunListener 通过监听事件2️⃣来调用
    4. 绑定 spring.main 前缀的 key value 至 SpringApplication
  7. Banner
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值