SpringBoot启动配置原理

SpringBoot启动配置原理

前言

上一篇:SpringBoot数据访问

下一篇:

一、配置原理

1.1 启动流程剖析

  • 首先在springboot项目的 run()上打个断点,跟着run方法一步步执行。
	@SpringBootApplication
public class CodeApplication {
	public static void main(String[] args) {
		SpringApplication.run(CodeApplication.class, args);
	}
}
  • 在第一步它首先对参数进行了封装:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }
  • 然后创建了 SpringApplication对象:
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.lazyInitialization = 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));
    //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    //从多个配置类中找到有main方法的主配置类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}
  • 创建的目的就是为了将配置的所有初始化器以及listener拿到并保存起来.

在这里插入图片描述

  • 创建完成之后执行run方法:
public ConfigurableApplicationContext run(String... args) {
    //初始化操作
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    //回调所有的获取SpringApplicationRunListener.starting()方法
    listeners.starting();
	
    Collection exceptionReporters;
    try {
        //封装命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //准备环境-----01
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //在控制台打印banner图标
        Banner printedBanner = this.printBanner(environment);
        //创建ApplicationContext;决定创建web的ioc还是普通的ioc
        context = this.createApplicationContext();
        //从类路径下META‐INF/spring.factories获取SpringBootExceptionReporter,原理同2中获取ApplicationContextInitializer和ApplicationListener
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        //准备上下文环境;将environment保存到ioc中 ----02
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat)
        //具体见https://blog.csdn.net/qq_33034955/article/details/105787420的嵌入式servlet修改原理
         //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
        this.refreshContext(context);
        //空方法
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
		//调用所有SpringApplicationRunListener的started方法
        listeners.started(context);
        //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调-----03
       //ApplicationRunner先回调,CommandLineRunner再回调
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        //调用所有SpringApplicationRunListener的running方法
        listeners.running(context);
        //整个SpringBoot应用启动完成以后返回启动的ioc容器;
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}
  • 上述子方法:

    • 准备环境—01
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        //获取环境参数
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        //配置环境参数
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        //执行
        ConfigurationPropertySources.attach((Environment)environment);
        创建环境完成后回调environmentPrepared();表示环境准备完成
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        //绑定环境参数
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }
    
        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }
    
    • 准备上下文环境----02
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        //将创建好的环境放到ioc容器中
            context.setEnvironment(environment);
        //注册
            this.postProcessApplicationContext(context);
        //获取所有的初始化器然后执行它的initializer()
            this.applyInitializers(context);
        //回调所有的SpringApplicationRunListener的contextPrepared();
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                this.logStartupInfo(context.getParent() == null);
                this.logStartupProfileInfo(context);
            }
    	//注册bean
            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());
            }
    
            Set<Object> sources = this.getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            this.load(context, sources.toArray(new Object[0]));
        //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
            listeners.contextLoaded(context);
        }
    
    • 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调—03
    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        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);
            }
        }
    
    }
    
  • 至此应用启动完成

1.2 几个重要的事件回调机制

  • 以下机制的回调调用均在上述代码中注释标注

  • 配置在META-INF/spring.factories

    ApplicationContextInitializer

    SpringApplicationRunListener

  • 放在ioc容器中

    ApplicationRunner

    CommandLineRunner

1.3 自定义测试

  • 这几个监听器均为接口,只需要创建各自的实习类,并配置一下即可。
  • ApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext con) {
        System.out.println("ApplicationContextInitializer....initialize...");
    }
}
  • SpringApplicationRunListener
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
    public MySpringApplicationRunListener(SpringApplication application, String[] args){

    }
    @Override
    public void starting() {
        System.out.println("SpringApplicationRunListener...starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        Object o = environment.getSystemProperties().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...");
    }
  • ApplicationRunner
@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run...");
    }
}
  • CommandLineRunner
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run...");
    }
}
  • 配置文件:注意一定要是放在META-INF下的spring.properties文件
org.springframework.context.ApplicationContextInitializer=\
com.atguigu.code.listener.MyApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.code.listener.MySpringApplicationRunListener
  • 运行结果测试

在这里插入图片描述

二、自定义starter

2.1 自定义准备

  • 模式:

    • 启动器只用来做依赖导入
    • 专门来写一个自动配置模块;
    • 启动器依赖自动配置模块,项目中引入相应的starter就会引入启动器的所有传递依赖

    1574561125363

  • 启动器

    • 启动器模块是一个空 JAR 文件,仅提供辅助性依赖管理,这些依赖可能用于自动 装配或者其他类库
  • 命名规范:

    • 官方命名

      spring-boot-starter-模块名

      egspring-boot-starter-webspring-boot-starter-jdbcspring-boot-starter-thymeleaf

    • 自定义命名

      模块名-spring-boot-starter

      egmybatis-spring-boot-start

  • 如何编写自动配置:

    • 自动配置:
    @Configuration //指定这个类是一个配置类
    @ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
    @AutoConfigureAfter //指定自动配置类的顺序
    @Bean //给容器中添加组件
    @ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
    @EnableConfigurationProperties //让xxxProperties生效加入到容器中
    public class XxxxAutoConfiguration {
    
    • 自动配置类要能加载,将需要启动就加载的自动配置类,配置在META-INF/spring.factories
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    

2.2 自定义案例

  • 自定义案例可以分为如下三部分:

    • 创建自动配置项目
    • 创建starter中间项目
    • 创建测试项目
  • 创建自动配置项目:

    • 自动配置类:
    package com.atguigu.starterspringbootautoconfigure.autoconfig;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ConditionalOnWebApplication //web应用才生效
    @EnableConfigurationProperties(StarterProperties.class) //让配置类生效,(注入到容器中)
    public class StarterAutoConfiguration {
    
        private final StarterProperties starterProperties;
    
        public StarterAutoConfiguration(StarterProperties starterProperties) {
            this.starterProperties = starterProperties;
        }
    
    
        @Bean
        public HelloService helloService() {
            return new HelloService();
        }
    
        public class HelloService {
    
            public String sayHello(String name) {
                return starterProperties.getPrefix() + name + starterProperties.getSuffix();
            }
        }
    }
    
    • 自动配置类:
    package com.atguigu.starterspringbootautoconfigure.autoconfig;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix = "atguigu.hello")
    public class StarterProperties {
    
        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;
        }
    }
    
    • 约束:
    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.2.6.RELEASE</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    	<groupId>com.atguigu</groupId>
    	<artifactId>starter-spring-boot-autoconfigure</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>starter-spring-boot-autoconfigure</name>
    	<description>Demo project for Spring Boot</description>
    
    	<properties>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<!--引入spring‐boot‐starter;所有starter的基本配置-->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter</artifactId>
    		</dependency>
    		<!--可以生成配置类提示文件-->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-configuration-processor</artifactId>
    			<optional>true</optional>
    		</dependency>
    
    	</dependencies>
    </project>
    
    • 配置文件:一定要放在resources文件夹下创建META-INF/spring.factories文件
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.atguigu.starterspringbootautoconfigure.autoconfig.StarterAutoConfiguration
    
  • 创建starter中间项目

    • 依赖:
    <?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.atguigu</groupId>
        <artifactId>starter-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
        <!--启动器-->
        <dependencies>
    
            <!--引入自动配置模块-->
            <dependency>
                <groupId>com.atguigu</groupId>
                <artifactId>starter-spring-boot-autoconfigure</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
        </dependencies>
    
    </project>
    
  • 创建测试项目

    • 依赖:
    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.2.6.RELEASE</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    	<groupId>com.atguigu</groupId>
    	<artifactId>starter-test</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>starter-test</name>
    	<description>Demo project for Spring Boot</description>
    
    	<properties>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    			<exclusions>
    				<exclusion>
    					<groupId>org.junit.vintage</groupId>
    					<artifactId>junit-vintage-engine</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    		<dependency>
    			<groupId>com.atguigu</groupId>
    			<artifactId>starter-spring-boot-starter</artifactId>
    			<version>1.0-SNAPSHOT</version>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    </project>
    
    • controller:
    package com.atguigu.startertest.controller;
    
    import com.atguigu.starterspringbootautoconfigure.autoconfig.StarterAutoConfiguration;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @Autowired
        private StarterAutoConfiguration.HelloService helloService;
    
    
        @RequestMapping("/hello")
        public String sayHello() {
            String hello = helloService.sayHello("xiaoming");
            return hello;
        }
    }
    
    • 配置文件:
    atguigu.hello.prefix=hello
    atguigu.hello.suffix=,world
    
  • 测试结果:
    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值