Springboot详解底层启动过程

目录
  • SpringApplication构造分析
  • SpringApplication run分析

SpringApplication构造分析

1、记录 BeanDefinition 源

spring容器刚开始是空的,要去各个源找到beanDefinition,这些源可能是配置类,可能是xml文件。在构造方法里会获取一个主源,也就是引导类,根据引导类去获取beanDefinition。

2、推断应用类型

根据jar包去判断是什么引用类型

3、记录 ApplicationContext 初始化器

对ApplicationContext做扩展

4、记录监听器

监听重要事件

5、推断主启动类

记录运行的主类。

SpringApplication run分析

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

发布 application starting 事件,在程序启动的重要节点发布事件

    public static void main(String[] args) throws Exception{
        // 添加 app 监听器
        SpringApplication app = new SpringApplication();
        app.addListeners(e -> System.out.println(e.getClass()));
        // 获取事件发送器实现类名
        List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());
        for (String name : names) {
            System.out.println(name);
            Class<?> clazz = Class.forName(name);
            Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);
            SpringApplicationRunListener publisher = (SpringApplicationRunListener) constructor.newInstance(app, args);
            // 发布事件
            DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
            publisher.starting(bootstrapContext); // spring boot 开始启动
            publisher.environmentPrepared(bootstrapContext, new StandardEnvironment()); // 环境信息准备完毕
            GenericApplicationContext context = new GenericApplicationContext();
            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

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

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"));
    }

4、ConfigurationPropertySources 处理(*)

发布 application environment 已准备事件

    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"));
    }
}

5、通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

application.properties,由 StandardConfigDataLocationResolver 解析

spring.application.json

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"));
    }
}

6、绑定 spring.main 到 SpringApplication 对象(*)

把配置文件中的值赋给SpringApplication的默认属性值

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("step6", new ClassPathResource("step6.properties")));
        System.out.println(application);
        Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
        System.out.println(application);
    }

7、打印 banner(*)

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);
    }
}

8、创建容器

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

9、准备容器发布

application context 已初始化事件

10、加载 bean 定义

发布 application prepared 事件

        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
        XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        reader1.register(Config.class);
        reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
        scanner.scan("com.itheima.a39.sub");

11、refresh 容器

发布 application started 事件

12、执行 runner

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

到此这篇关于Springboot详解底层启动过程的文章就介绍到这了,更多相关Springboot启动过程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot启动过程中常用的回调示例详解

    目录 1.介绍 2.常用的拓展接口 3.实例: 4.其他拓展接口: 1.介绍 springboot提供非常丰富回调接口,利用这些接口可以做非常多的事情,对于一些常用的回调接口进行介绍 2.常用的拓展接口 1.ApplicationContextInitializer2.ApplicationListener3.ApplicationRunner4.CommandLineRunner 3.实例: 1.ApplicationContextInitializer接口是在spring容器初初始化之前调用

  • SpringBoot启动过程的实现

    SpringBoot启动过程分析,首先打开SpringBoot的启用入口Main类: @SpringBootApplication public class ApplicationMain{ public static void main(String[] args) { SpringApplication.run(ApplicationMain.class, args); } } 可以看到main方法里面只有一行核心启用类:SpringApplication.run(ApplicationMa

  • SpringBoot应用启动过程分析

    SpringBoot项目通过SpringApplication.run(App.class, args)来启动: @Configuration public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } 接下来,通过源码来看看SpringApplication.run()方法的执行过程.如果对源码不感兴趣,直接下拉到文章末尾,看启动框图. 1.调用S

  • SpringBoot整个启动过程的分析

    前言 前一篇分析了SpringBoot如何启动以及内置web容器,这篇我们一起看一下SpringBoot的整个启动过程,废话不多说,正文开始. 正文 一.SpringBoot的启动类是**application,以注解@SpringBootApplication注明. @SpringBootApplication public class CmsApplication { public static void main(String[] args) { SpringApplication.run

  • Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解

    标题和Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动很像,所以特别强调一下,这个是Tomcat对象的. 从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布诸如configure_start.before_init.after_start的lifecycleEvent事件给相应的监听器(如

  • Springboot详解底层启动过程

    目录 SpringApplication构造分析 SpringApplication run分析 SpringApplication构造分析 1.记录 BeanDefinition 源 spring容器刚开始是空的,要去各个源找到beanDefinition,这些源可能是配置类,可能是xml文件.在构造方法里会获取一个主源,也就是引导类,根据引导类去获取beanDefinition. 2.推断应用类型 根据jar包去判断是什么引用类型 3.记录 ApplicationContext 初始化器 对

  • SpringBoot详解整合MyBatis过程中可能遇到的问题

    尽量不要用 jUnit 提供的单元测试 提一个要求尽量使用SpringBoot 提供的测试类进行测试,能够自动扫描组件以及使用容器中的bean对象 还有如果有组件 中存在注入对象的话,那么必须在SpringBoot容器中取出 这个组件,进而使用注入的对象的功能!!! 今天有个错误,花了很长时间来解决,最后发现是一个很低级很基础的错误! 这是mapper接口,使用@mapper 相当于将接口的代理对象注册进入bean中,但是上下文中找不到(其实是正常) 因为 @Mapper 这个注解是 Mybat

  • Springboot详解如何实现SQL注入过滤器过程

    目录 1.过滤器SqlInjectFilter 2.请求装饰类CustomRequestWrapper 3.过滤器注册 4.测试辅助类 4.1 结果对象ResultObj 4.2 Restful的Controller类 5.测试 5.1 POST请求测试 5.2 GET请求测试1 5.3 GET请求测试2 场景:以过滤器(Filter)的方式,对所有http请求的入参拦截,使用正则表达式匹配入参中的字符串.存在SQL注入风险的参数,中断请求,并立即返回提示信息.不存在SQL注入风险的参数,校验通

  • 详解Android系统启动过程

    计算机是如何启动的 计算机的硬件包括:CPU,内存,硬盘,显卡,显示器,键盘鼠标等输入输出设备.所有的软件都是存放在硬盘中,程序执行时,需要将程序从硬盘上读取到内存中,然后加载到CPU中来运行.当按下开机键时,内存中什么都没有,因此需要借助某种方式,将操作系统加载到内存中,而完成这项任务的就是BIOS. 引导阶段 BIOS:BIOS是主板芯片上的一个程序,计算机通电后,第一件事情就是读取BIOS. BIOS首先进行硬件检测,检查计算机硬件能否满足运行的基本条件.如果硬件出现问题,主板发出不同的蜂

  • SpringBoot详解如何实现读写分离

    目录 前言 1.项目引入依赖 2.yml配置 3.启动 4.测试 5.中间所遇到的问题 前言 根据公司业务需求,项目需要读写分离,所以记录下读写分离的过程. 分为两个部分: 1.项目的读写分离. 2.mysql数据库的主从复制. 本篇使用的依赖包为sharding-jdbc-spring-boot-starter,也有考虑直接用dynamic-datasource-spring-boot-starter,但是需要在程序中显式的声明所指定的数据源,并且在从库>=2 的时候需要自己写算法进行读库的选

  • SpringBoot详解整合Spring Boot Admin实现监控功能

    目录 监控 监控的意义 可视化监控平台 监控原理 自定义监控指标 监控 ​ 在说监控之前,需要回顾一下软件业的发展史.最早的软件完成一些非常简单的功能,代码不多,错误也少.随着软件功能的逐步完善,软件的功能变得越来越复杂,功能不能得到有效的保障,这个阶段出现了针对软件功能的检测,也就是软件测试.伴随着计算机操作系统的逐步升级,软件的运行状态也变得开始让人捉摸不透,出现了不稳定的状况.伴随着计算机网络的发展,程序也从单机状态切换成基于计算机网络的程序,应用于网络的程序开始出现,由于网络的不稳定性,

  • 详解Nginx启动失败的几种错误处理

    使用Nginx做Web服务器过程中,碰到过以下几个问题: 1.nginx启动失败 systemctl start nginx.service 启动nginx失败,报错信息如下: Starting nginx: nginx: [emerg] bind() to 0.0.0.0:**** failed (13: Permission denied) 这通常是因为开启了SELinux的原因,使用命令 getenforce 可以查看SELinux状态,如果输出为 enforcing 表示已开启.用以下方

  • 详解nginx安装过程并代理下载服务器文件

    主旨 nginx服务,现在是最好用的一种代理服务,日常我们代理的一般都是端口转发和流量控制,那么我们如何通过nginx来实现文件下载呢?就是通过nginx服务,通过url来下载文件. Nginx是一款轻量级的web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行.其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好. 环境 linux服务器nginx软件 软件下载 链接: https://pan.baidu.co

  • SpringBoot详解如果通过@Value注解给静态变量注入值

    目录 前序 方案一 方案二 方案三 使用场景 总结 最近做项目的时候,给static变量赋值, 使用 @value注解 ,结果 获取一直为null , 1.spring不允许/不支持把值注入到静态变量中 2.Spring的@Value依赖注入是依赖set方法 3.set方法是普通的对象方法 4.static变量是类的属性,static没有set方法 前序 SpringBoot中使用@Value()只能给普通变量注入值,不能直接给静态变量赋值 例如,application-dev.properti

  • Springboot详解RocketMQ实现广播消息流程

    RocketMQ消息模式主要有两种:广播模式.集群模式(负载均衡模式) 广播模式是每个消费者,都会消费消息: 负载均衡模式是每一个消费只会被某一个消费者消费一次: 我们业务上一般用的是负载均衡模式,当然一些特殊场景需要用到广播模式,比如发送一个信息到邮箱,手机,站内提示: 我们可以通过@RocketMQMessageListener的messageModel属性值来设置,MessageModel.BROADCASTING是广播模式,MessageModel.CLUSTERING是默认集群负载均衡

随机推荐