SpringBoot的啓動配置原理

1、啓動流程

  1. 建立SpringApplication對象
public class SpringApplication {
 
    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 保存主配置類
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判斷當前是否一個web應用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();                                                            
         // 從類路徑下找到META-INF/spring.factories配置的全部ApplicationContextInitializer;而後保存起來。
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 從類路徑下找到META-INF/spring.factories配置的全部ApplicationListener
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //從多個配置類中找到有main方法的主配置類
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
  1. 運行run方法
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        // 獲取SpringApplicationRunListener; 從類路徑下META-INF/spring.factories獲取
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 回調全部的獲取SpringApplicationRunListener.starting()方法
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 封裝命令行參數
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 準備環境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 建立環境完成後回調SpringApplicationRunListener.environmentPrepared();表示環境準備完成
            Banner printedBanner = this.printBanner(environment);
            // 建立ApplicationContext;決定建立web的IOC仍是普通的IOC
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 準備上下文環境;將environment保存到IOC中,並且applyInitializers();
            // 並且applyInitializers():回調以前保存的全部ApplicationContextInitializer的initialize方法
            // 回調全部的SpringApplicationRunListener的contextPrepared();
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // prepareContext運行完成之後回調全部的SpringApplicationRunListener的contextLoaded();
            // 掃描容器;IOC容器初始化(若是是web應用還會建立嵌入的Tomcat);
            // 掃描,建立,加載全部組件的地方(配置類,組件,自動配置)
            this.refreshContext(context);
            // 從IOC容器中獲取全部的ApplicationRunner和CommandLineRunner進行回調
            // ApplicationRunner先回調,CommandLineRunner再回調
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

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

        try {
            listeners.running(context);
            // 整個SpringBoot應用啓動完成之後返回啓動的IOC容器
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
  1. 事件監聽機制
    須要配置在META-INF/spring.factories中的事件監聽器。
    ApplicationContextInitializer
public class CustomApplicationContextInitializer implements 
        ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("ApplicationContextInitializer.....initialize");
    }
}

SpringApplicationRunListenerjava

public class CustomSrpingApplicationRunListener implements SpringApplicationRunListener {
    
    // 必須有的構造器
    public CustomSrpingApplicationRunListener(SpringApplication application,String[] args){
        
    }
    @Override
    public void starting() {
        System.out.println("SpringApplicationRunListener...starting....");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        Object o = environment.getSystemEnvironment().get("os.name");
        System.out.println("SpringApplicationRunListener...environmentPrepared...."+o);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener..contextPrepared");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener..contextLoaded");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener..started");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener..running");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("SpringApplicationRunListener..failed");
    }
    
}

配置(META-INF/spring.factories)web

org.springframework.context.ApplicationContextInitializer=com.desperado.demo.CustomApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=com.desperado.demo.CustomSrpingApplicationRunListener

只須要放在IOC容器中的監聽器。
ApplicationRunnerspring

@Component
public class CustomApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        System.out.println("ApplicationRunner ... run....");
    }
}

CommandLineRunnerapache

@Component
public class CustomCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        System.out.println("commandLineRunner ... run ...");
    }
}

2、自定義starter

  1. 要使用到的註解
    @Configuration 指定類是一個配置類
    @ConditionalOnXXX 在指定條件成立的狀況下自動配置生效、
    @AutoConfigureAfter 在特定自動裝配class以後(指定自動配置類的順序)
    @AutoConfigureBefore 在特定自動裝配class以前(指定自動配置類的順序)
    @AutoConfigureOrder 指定順序
    @Bean 給容器中添加組件
    @ConfigurationPropertie 結合相關xxxProperties類來綁定相關的配置。
    @EnableConfigurationProperties 讓xxxProperties生效加入到容器中。app

  2. 加載方式
    自動配置類要能加載,將須要啓動就加載的自動配置類,配置在META-INF/spring.factories文件中。less

  3. 啓動器模式
    啓動器只用來作依賴導入,專門寫一個自動配置模塊。啓動器依賴自動配置,使用時只須要引入啓動器(starter)。maven

  4. 啓動器規則
    啓動器就是個空jar文件,僅提供輔助性依賴管理,這些依賴可能用於自動裝配或者其餘庫。ide

命名規範:
- 推薦使用如下命名規範
- 官方命名空間
 - 前綴:spring-boot-starter-
 - 模式:spring-boot-starter-模塊名
- 自定義命名空間
  - 後綴:-spring-boot-starter
  - 模式:模塊名-spring-boot-starterspring-boot

  1. 編寫一個啓動器模塊
    1). 啓動器模塊
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.desperado.starter</groupId>
    <artifactId>desperado-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!-- 啓動器 -->
    <dependencies>
        <!-- 引入自動配置模塊 -->
        <dependency>
            <groupId>com.desperado.starter</groupId>
            <artifactId>desperado-spring-boot-starter-autoconfigurer</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>

2). 自動配置模塊ui

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.desperado.starter</groupId>
    <artifactId>desperado-spring-boot-starter-autoconfigurer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <!--  引入spring-boot-starter;全部starter的基本配置 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

    </dependencies>

</project>

3). 編寫配置文件類

@ConfigurationProperties(prefix = "desperado.custom")
public class CustomProperties {
    
    private String prefix;
    
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

4). 進行配置文件的處理

public class CustomService {
    CustomProperties customProperties;

    public CustomProperties getCustomProperties(){
        return customProperties;
    }

    public void setCustomProperties(CustomProperties customProperties){
        this.customProperties = customProperties;
    }

    public String sayHello(String name){
        return customProperties.getPrefix()+"-"+name+customProperties.getSuffix();
    }
}

5). 編寫自動配置類

@ConditionalOnWebApplication
@EnableConfigurationProperties(CustomProperties.class)
public class CustomServiceAutoConfiguration {
    
    @Autowired
    CustomProperties customProperties;
    
    @Bean
    public CustomService customService(){
        CustomService customService = new CustomService();
        customService.setCustomProperties(customProperties);
        return customService;
    }
}
相關文章
相關標籤/搜索