Skip to content

https://cloud.baidu.com/article/3318850https://blog.csdn.net/Andya_net/article/details/131188311

❤ SpringBoot 的启动流程

启动类的 main 方法

  • 首先,从一个标注了 @SpringBootApplication 的 main 方法开始
  • 运行 run() 方法,把主类,和 args 参数传递进去
java
@SpringBootApplication
public class KafkaApplication {
    public static void main(String[] args) {
        SpringApplication.run(KafkaApplication.class, args);
    }
}

创建 ApplicationContext

根据应用类型,创建相应的 ApplicationContext

刷新 ApplicationContext

加载并初始化所有的 Bean

自动配置

根据依赖,和 application 配置文件进行自动配置

启动内嵌服务器

如果是 Web 应用,则启动

执行 CommandLineRunner 和 ApplicationRunner

ApplicationRunner 是 SpringBoot 的启动运行器,它会在应用程序启动完成后执行一些初始化操作

java
@Component
public class KafkaProducerConfig {
    // 在启动时,执行一些操作
    @Bean
    public ApplicationRunner runner(KafkaTemplate<String, String> kafkaTemplate) {
        return args -> {
            kafkaTemplate.send("TopicTwo", "Hello World");
        };
    }
}

❤️ 环境上下文

SpringBoot 在启动后,在不同的运行阶段会设置扩展点,我们可以根据需求在指定阶段,执行自定义的代码

ApplicationContext

[!quote] ApplicationContext【应用上下文】 ApplicationContext 是 Spring 核心接口之一,是 Spring 中实现 IOC 和 DI 的容器,用于存储应用程序运行时所需的各种 Bean 对象,并负责实例化,配置,组装这些 Bean【当一个 Bean 需要另一个 Bean 作为依赖时,ApplicationContext 就会负责寻找和注入相应的 Bean】

一旦 `ApplicationContext` 已经刷新,Bean 的定义就不能再被修改了,**但是,你应该尽量避免在运行时修改 Bean 的定义,而是应该在配置阶段就确定好 Bean 的定义**

ApplicationContext 包含一个 Environment,这个 Environment 保存了所有跟应用程序运行环境有关的属性【系统环境变量,JVM 系统属性,命令行参数,应用程序配置文件……】

ApplicationContextInitializer

[!quote] ApplicationContextInitializerApplicationContextInitializer 就是 SpringBoot 众多扩展点中的一个,在 IOC 容器创建完成后ApplicationContext 刷新前【完全初始化 Bean 之前】执行

[!hint] ApplicationContextInitializer 的作用

对上下文环境做一些操作:

  • 动态改变 Spring 配置属性【修改 Bean 定义】
  • 修改 ApplicationContext 的环境属性

你可以根据运行环境的不同,动态地修改应用程序的配置【你可能在开发环境和生产环境中使用不同的数据库配置,或者在不同的服务器上运行时使用不同的服务端口 ……】

具体实现

  • 创建一个类,实现 ApplicationContextInitializer 接口,并继承 initialize 方法
java
package com.example.web_2.initializer;

public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 准备属性
        Map<String, Object> myMap = new HashMap<>();
        myMap.put("applicationName", "big-events");

        // 获取环境对象
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        // 获取属性源
        MutablePropertySources propertySources = environment.getPropertySources();

        // 添加属性
        propertySources.addLast(new MapPropertySource("myPropertySource", myMap));
    }
}
  • META-INF/spring.factories 中注册这个初始化器
factories
# 接口的全类名=实现类的全类名
org.springframework.context.ApplicationContextInitializer=com.example.web_2.initializer.MyApplicationContextInitializer
  • 测试一下
java
@SpringBootApplication
public class Web2Application {
    public static void main(String[] args) {
        // 获取上下文对象
        ConfigurableApplicationContext run = SpringApplication.run(Web2Application.class, args);
        String applicationName = run.getEnvironment().getProperty("applicationName");
        System.out.println("applicationName = " + applicationName);
    }
}

---
applicationName = big-events

❤ 禁用依赖

有时我们想快速启动项目,不使用某些在 Maven 中引入的依赖,比如数据库,消息队列 …… ,我们可以直接禁用掉用到这些依赖的 Bean 加载


  • 在启动类加上 exclude ,就能快速禁用某些依赖的自动加载
java
@SpringBootApplication(exclude = {RocketMQAutoConfiguration.class})
  • 禁用了 RocketMQ Bean 的初始化后,我们还要对用到这个 Bean 的地方进行 @Lazy 懒加载,让其不会因为缺失 Bean 而报错
java
public class MQProducer {
    @Autowired
    @Lazy
    private RocketMQTemplate rocketMQTemplate;
}

❤️ 线程池

NOTE

Spring 默认有一个 main 主线程,这个线程是用来启动 Spring 和初始化 App 资源的。Spring 主要依靠 Servlet 容器中的线程池来处理大量的并发用户请求

如果你需要执行异步任务,但是你又不单独配置异步线程池,那么异步任务就会和 Servlet 容器中的线程抢夺资源

💛 异步线程池

异步执行线程池只专注于异步任务执行,需要加上 @EnableAsync

java
@Configuration
public class AsyncTaskConfig {  
    @Bean(name = "myAsyncExecutorThreadPool")  
    public Executor myAsyncExecutorThreadPool() {  
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
        executor.setCorePoolSize(30);  // 核心线程数  
        executor.setMaxPoolSize(50);  // 最大线程数  
        executor.setQueueCapacity(100);  // 队列容量  
        executor.setThreadNamePrefix("MyAsyncExecutorThreadPool - ");  
        executor.initialize();  
        return executor;  
    }  
}
java
// 使用
@Async("myAsyncExecutorThreadPool")
public Future<String> asyncTask() {
	// 模拟耗时任务
	Thread.sleep(2000);
	System.out.println("异步任务执行完毕");
	return new AsyncResult<>("Task Completed");
}

❤️ 虚拟线程

❤️ 其他操作

将 tomcat 改为 undertow :undertow 比 tomcat 更适合高并发

  • 更新依赖
xml
<dependencies>
    <!-- 移除 Spring Boot 默认的 tomcat -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- 添加 Undertow -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
</dependencies>
  • yml 配置
yml
server:
  port: 8080
  undertow:
    io-threads: 8
    worker-threads: 64
    buffer-size: 1024
    direct-buffers: true