首页 > 其他分享 >Spring原理Boot

Spring原理Boot

时间:2024-11-04 22:43:40浏览次数:3  
标签:Spring Boot System class static context 原理 new public

Spring原理 SpringBoot

1 Boot

1.1 Boot 骨架项目

如果是 linux 环境,用以下命令即可获取 spring boot 的骨架 pom.xml

curl -G https://start.spring.io/pom.xml -d dependencies=web,mysql,mybatis -o pom.xml

也可以使用 Postman 等工具实现

若想获取更多用法,请参考

curl https://start.spring.io

1.2 Boot War项目

步骤1:创建模块,区别在于打包方式选择 war

image-20241016211334908

接下来勾选 Spring Web 支持

image-20241016211357080

步骤2:编写控制器

@Controller
public class MyController {

    @RequestMapping("/hello")
    public String abc() {
        System.out.println("进入了控制器");
        return "hello";
    }
}

步骤3:编写 jsp 视图,新建 webapp 目录和一个 hello.jsp 文件,注意文件名与控制器方法返回的视图逻辑名一致

src
	|- main
		|- java
		|- resources
		|- webapp
			|- hello.jsp

步骤4:配置视图路径,打开 application.properties 文件

spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

将来 prefix + 控制器方法返回值 + suffix 即为视图完整路径

测试

如果用 mvn 插件 mvn spring-boot:run 或 main 方法测试

  • 必须添加如下依赖,因为此时用的还是内嵌 tomcat,而内嵌 tomcat 默认不带 jasper(用来解析 jsp)
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>provided</scope>
</dependency>

也可以使用 Idea 配置 tomcat 来测试,此时用的是外置 tomcat

  • 骨架生成的代码中,多了一个 ServletInitializer,它的作用就是配置外置 Tomcat 使用的,在外置 Tomcat 启动后,去调用它创建和运行 SpringApplication

启示

对于 jar 项目,若要支持 jsp,也可以在加入 jasper 依赖的前提下,把 jsp 文件置入 META-INF/resources

1.3 Boot 启动过程

阶段一:SpringApplication 构造

演示获取 Bean Definition 源

@Configuration
public class A39_1 {
    public static void main(String[] args) {
        System.out.println("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(A39_1.class);
        spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
        System.out.println("2. 演示推断应用类型");
        System.out.println("3. 演示 ApplicationContext 初始化器");
        System.out.println("4. 演示监听器与事件");
        System.out.println("5. 演示主类推断");

        //在run方法中创建并初始化spring容器
        ConfigurableApplicationContext context = spring.run(args);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }
        context.close();
    }

    static class Bean1 {

    }

    static class Bean2 {

    }

    static class Bean3 {

    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }

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

image-20241020221531909

image-20241020221610600

image-20241020221757203

演示推断应用类型

image-20241020222022322

image-20241020222439282

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    System.out.println("1. 演示获取 Bean Definition 源");
    SpringApplication spring = new SpringApplication(A39_1.class);
    spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
    System.out.println("2. 演示推断应用类型");
    Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
    deduceFromClasspath.setAccessible(true);
    //static WebApplicationType deduceFromClasspath() {} 静态方法,无需使用对象,所以传null,同时也不需要参数
    System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
    System.out.println("3. 演示 ApplicationContext 初始化器");
    System.out.println("4. 演示监听器与事件");
    System.out.println("5. 演示主类推断");

    //在run方法中创建并初始化spring容器
    ConfigurableApplicationContext context = spring.run(args);
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
    }
    context.close();
}

image-20241020223349537

image-20241020223429912

image-20241020223619346

演示 ApplicationContext 初始化器

image-20241020224101670

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    System.out.println("1. 演示获取 Bean Definition 源");
    SpringApplication spring = new SpringApplication(A39_1.class);
    spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
    System.out.println("2. 演示推断应用类型");
    Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
    deduceFromClasspath.setAccessible(true);
    //static WebApplicationType deduceFromClasspath() {} 静态方法,无需使用对象,所以传null,同时也不需要参数
    System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
    System.out.println("3. 演示 ApplicationContext 初始化器");
    spring.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {//刚刚创建,但是没有refresh的applicationContext对象
            if (applicationContext instanceof GenericApplicationContext gac) { //转换类型
                gac.registerBean("bean3",Bean3.class);//bean1,bean2之前注册了
            }
        }
    });
    System.out.println("4. 演示监听器与事件");
    System.out.println("5. 演示主类推断");

    //在run方法中创建并初始化spring容器
    ConfigurableApplicationContext context = spring.run(args);

    // 创建 ApplicationContext
    // 调用初始化器 对 ApplicationContext 做扩展
    // ApplicationContext.refresh()

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

image-20241020224908063

image-20241020224946227

演示监听器与事件

image-20241020225251054

image-20241020225722758

演示主类推断

image-20241020225946898

image-20241020230416580

阶段二:执行 run 方法

事件发布器 (run 1)

image-20241022224144084

image-20241022224217253

image-20241022224253359

public class A39_2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //添加 app 监听器
        SpringApplication app = new SpringApplication();
        app.addListeners(e -> System.out.println(e.getClass()));

        // 获取事件发送器实现类名
        //参数1: 接口类型 , 参数2: ClassLoader  返回值:names:多个实现类的名字
        List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());
        for (String name : names) {
            //org.springframework.boot.context.event.EventPublishingRunListener: 拿到了实现类的名字
            System.out.println(name);
            Class<?> clazz = Class.forName(name);//拿到类对象
            Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);//拿到类构造器
            //事件发布器对象创建完成
            EventPublishingRunListener publisher = (EventPublishingRunListener) 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 启动出错
        }
    }
}

image-20241022224412592

run 方法流程( 8 -11 )

配置类获取bean 定义

public class A39_3 {
    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        //新增一个初始化器
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override             //需要提前准备容器,所以需要在第9.步骤回调这个初始化方法
            public void initialize(ConfigurableApplicationContext applicationContext) {

            }
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 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 定义");
        //设置将来读取的bean definition 存储位置:getDefaultListableBeanFactory ,还没有开始读取bean definition
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context.getDefaultListableBeanFactory());
        //解析Config类中bean 定义,并加入到bean工厂
        reader.register(Config.class);

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

        /*
            学到了什么
            a. 创建容器、加载 bean 定义、refresh, 对应的步骤

         */
    }

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

    static class Bean4 {

    }

    static class Bean5 {

    }

    static class Bean6 {

    }

    @Configuration
    static class Config {
        @Bean
        public Bean5 bean5() {
            return new Bean5();
        }

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

    }
}

image-20241022230447375

xml文件获取类定义

image-20241022230959645

//xml获取类定义
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));

image-20241022231035364

包扫描获取类定义

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com/feng/a39boot/sub");//扫描包路径下所有类,并解析bean定义,加入到bean工厂

image-20241024204627535

run (2,12)

@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());// 获取带--的key、
                System.out.println(args.getOptionValues("server.port"));// 获取带--的key对应的值
                System.out.println(args.getNonOptionArgs());// 获取不带--的参数
            }
        };
    }
}

image-20241024212301015

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
//args: main()方法的参数
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

image-20241024212334780

image-20241024212455899

image-20241024212558818

image-20241024212629844

完整代码

public class A39_3 {
    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        //新增一个初始化器
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override             //需要提前准备容器,所以需要在第9.步骤回调这个初始化方法
            public void initialize(ConfigurableApplicationContext applicationContext) {

            }
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
        //args: main()方法的参数
        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 定义");
        //设置将来读取的bean definition 存储位置:getDefaultListableBeanFactory ,还没有开始读取bean definition
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
        //解析Config类中bean 定义,并加入到bean工厂
        reader1.register(Config.class);
        //xml获取类定义
        XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
        reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        scanner.scan("com/feng/a39boot/sub");//扫描包路径下所有类,并解析bean定义,加入到bean工厂

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
        context.refresh();//必须要先刷新,才能验证

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

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");
        /**
         * springboot执行结束时会回调CommandLineRunner,ApplicationRunner
         */
        //getBeansOfType: 根据类型去拿bean  , K: bean的名字  V: bean的实例对象
        Map<String, CommandLineRunner> beansOfType = context.getBeansOfType(CommandLineRunner.class);
        for (CommandLineRunner runner : beansOfType.values()) {
            runner.run(args);
        }

        for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class)
                .values()) {
            runner.run(arguments); //applicationArguments: 第二步封装好的参数
        }

        /*
            学到了什么
            a. 创建容器、加载 bean 定义、refresh, 对应的步骤

         */
    }

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

    static class Bean4 {

    }

    static class Bean5 {

    }

    static class Bean6 {

    }

    @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());// 获取带--的key、
                    System.out.println(args.getOptionValues("server.port"));// 获取带--的key对应的值
                    System.out.println(args.getNonOptionArgs());// 获取不带--的参数
                }
            };
        }
    }
}

run 3

public class Step3 {
    public static void main(String[] args) throws IOException {
        ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
        for (PropertySource<?> ps : env.getPropertySources()) {//来源的集合
            /** 打印结果
             * PropertiesPropertySource {name='systemProperties'}  系统属性
             * SystemEnvironmentPropertySource {name='systemEnvironment'} 系统环境变量
             *
             * 如果找到的同名的key, 系统属性优先级高
             */
            System.out.println(ps);
        }

        System.out.println(env.getProperty("JAVA_HOME"));
    }
}

image-20241024213735789

image-20241024220149784

image-20241024220344166

添加配置文件来源

image-20241024220828318

添加命令行来源

image-20241024221256130

image-20241024221340684

public class Step3 {
    public static void main(String[] args) throws IOException {
        ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
        //优先级添加到最后
        env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("application.properties")));
        //命令行添加来源
        env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
        for (PropertySource<?> ps : env.getPropertySources()) {//来源的集合
            /** 打印结果
             * PropertiesPropertySource {name='systemProperties'}  系统属性
             * SystemEnvironmentPropertySource {name='systemEnvironment'} 系统环境变量
             *
             * 如果找到的同名的key, 系统属性优先级高
             */
            System.out.println(ps);
        }

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

run 4

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);//加入到evn中,优先级最高
        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"));
    }
}

image-20241024222412066

run 5

后置处理器增强添加源

public class Step5 {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication();
        ApplicationEnvironment env = new ApplicationEnvironment();

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        //EnvironmentPostProcessor(后置处理器) 的实现类
        ConfigDataEnvironmentPostProcessor postProcessor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
        postProcessor1.postProcessEnvironment(env, app);//添加一些propertySource
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后1");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        System.out.println(env.getProperty("server.port"));

        RandomValuePropertySourceEnvironmentPostProcessor postProcessor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
        postProcessor2.postProcessEnvironment(env, app);//添加一些propertySource
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后2");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        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"));
    }

}

image-20241024224552467

image-20241024224631623

之前都是使用硬编码方法,springboot都是使用配置文件

image-20241024225624906

image-20241024225658353

image-20241024225745513

spring通过监听器实现上面步骤

image-20241024230131945

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

        //run 的第3,4步,创建envirment对象,初始化一些属性,第5步才可以添加监听器,所以第5步,添加一个事件发布器触发监听事件
        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);
        }

    }

image-20241024231400041

run 6

image-20241028210315707

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: 代表获取的源头  bind: 代表绑定,第一参数是properties中的key值前缀,第二个参数是实体类型  get:获取绑定赋值后的对象
        User user = Binder.get(env).bind("user", User.class).get();
        System.out.println(user);
    }

    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 + '\'' +
                   '}';
        }
    }
}

image-20241028212406914

User user = new User();
Binder.get(env).bind("user", Bindable.ofInstance(user));//现有对象绑定
System.out.println(user);

image-20241028214316479

image-20241028215049836

// 绑定 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: 代表获取的源头  bind: 代表绑定,第一参数是properties中的key值前缀,第二个参数是实体类型  get:获取绑定赋值后的对象
    /*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);*/

    env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));
    Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
    System.out.println(application);
}

step6主要是把spring.main中的属性和配置文件中的值绑定

run 7

输出图片

image-20241028222342387

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

run 方法小结

源码

image-20241028222824116

image-20241028223605258

image-20241029195759237

image-20241029200637354

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    ConfigurableApplicationContext context = null;
    this.configureHeadlessProperty();
    //1步骤:根据spring.factory读取里面的事件发布器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    //springboot开始启动了
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        //2步骤:分装main参数,分成一个 --符号的选项参数 和一个非选项参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //查看下方源码方法
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //7步骤:打印banner信息
        Banner printedBanner = this.printBanner(environment);
        //8步骤:创建spring容器,根据在构造方法中推断出来的容器类型,在三种类型中,选择一种容器实现
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        //查看下方源码
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        //调用容器refresh方法,调用各种bean工厂后处理器,准备各种bean的后处理器。然后初始化每个单例
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
        }
		//发布一个started事件
        listeners.started(context, timeTakenToStartup);
        //查看下方源码
        this.callRunners(context, applicationArguments);
    } catch (Throwable var12) {
        this.handleRunFailure(context, var12, listeners);
        throw new IllegalStateException(var12);
    }

    try {
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        //发布一个ready事件,表明springboot项目启动完成
        listeners.ready(context, timeTakenToReady);
        return context;
    } catch (Throwable var11) {
        //如果出现异常,发布一个失败事件
        this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var11);
    }
}


    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        //3步骤:创建environment环境   如果非web环境,创建applicationContext对象
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        //把参数信息封装成properties源数据对象,添加到environment中(environment从参数中获取一些键值(只能是选项参数--符号)信息)
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        //4步骤:把参数中key中以_分隔或者驼峰分隔的,转成-分隔
        ConfigurationPropertySources.attach((Environment)environment);
        //5步骤:事件的发布与响应,为environmen对象添加源
        listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
        DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
        Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
        //6步骤:把environment中spring.main前缀和springapplication对象绑定
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = this.convertEnvironment((ConfigurableEnvironment)environment);
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }


    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        //9步骤:应用初始化器,对applicationContext做功能增强
        this.applyInitializers(context);
        //发布contextPrepared事件,容器创建好,并初始化容器后,发布这个事件
        listeners.contextPrepared(context);
        bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
        //第10步骤,获取所有beanDefination源
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        //把获取的源加载到容器,加载各种beanDefination
        this.load(context, sources.toArray(new Object[0]));
        //发布contextLoaded事件
        listeners.contextLoaded(context);
    }


    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        //12步骤,调用所有实现了ApplicationRunner.class或者是CommandLineRunner.class的bean
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }

    }

boot启动过程总结

阶段一: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️⃣

带 * 的有独立的示例

演示 - 启动过程

a39.A39_1 对应 SpringApplication 构造

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

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

收获

标签:Spring,Boot,System,class,static,context,原理,new,public
From: https://www.cnblogs.com/fengpeng123/p/18526904

相关文章

  • 设计原理上
    Java设计模式1前言1.1目的2七大原则2.1单一职责原则方案一packagecom.feng.principle.singleresponsibility;/***@Authorfengpeng*@Date2023/4/12*@Time22:53*/publicclasssingleResponsibility01{publicstaticvoidmain(String[]args......
  • 【鸿蒙南向】移植案例与原理 - build lite源码分析 之 hb命令__main__.py
    ......
  • 【鸿蒙南向】移植案例与原理 - HPM包描述文件bundle.json
    ......
  • 计算机毕业设计java基于springboot的网上书店系统
    文章目录项目介绍技术介绍功能介绍核心代码数据库参考系统效果图项目介绍  本文致力于探讨基于SpringBoot框架的网上书店系统的全面设计与实现。随着网络技术的迅猛发展,网上书店作为一种便捷的购书方式受到了广泛关注。为了满足用户对于购书的需求,本文首先从用......
  • Golang channel底层原理
    1原理默认情况下,读写未就绪的channel(读没有数据的channel,或者写缓冲区已满的channel)时,协程会被阻塞。但是当读写channel操作和select搭配使用时,即使channel未就绪,也可以执行其它分支,当前协程不会被阻塞。ch:=make(chanint)select{case<-ch:default:}本文......
  • EPS原理笔记
    EPSUE(userequipment),移动用户设备LTE(LongTermEvolution),无线接入网部分,E-UTRANEPC(systemArchitectureEvolution、EvoloedPacketCore),核心网部分,主要包括MME、S-GW、P-GW、HSS,连接Internet等外部PDN(PacketDataNetwork)3GPP定义3GPP接入是指遵循3GPP制定......
  • Springboot创建Mave聚合工程(可灵活创建任意版本)
    文章目录前言1需要的环境与项目结构2Idea新建Maven工程3SpringBoot引入并测试4聚合工程测试5springboot3版本引入6不同环境启动项目前言请注意,从2022年第四季度发布的Spring6框架开始,最低Java版本要求已提升至JDK17。Spring6对内部架构进行了重构,并将......
  • Java毕设项目案例实战II基于Java+Spring Boot+Mysql的果蔬作物疾病防治系统的设计与实
    目录一、前言二、技术介绍三、系统实现四、核心代码五、源码获取全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末一、前言随着现代农业的快速发展,果蔬作物的健康生长与高效管理......
  • Java毕设项目案例实战II基于Java+Spring Boot+Mysql的公司资产网站设计与实现(开发文档
    目录一、前言二、技术介绍三、系统实现四、核心代码五、源码获取全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末一、前言在当今信息化高速发展的时代,企业资产的高效管理和精确......