程序员社区

详细SpringBoot教程之启动配置原理

Hello,欢迎来到程序员社区。 今天聊一聊 详细SpringBoot教程之启动配置原理,希望对大家有所帮助。

Java面试手册PDF下载:点击下载最全Java面试手册

                
                

写在前面

鉴于有人留言说想要学习SpringBoot相关的知识,我这里打算写一个SpringBoot系列的相关博文,目标呢是想让看了这一系列博文的同学呢,能够对SpringBoot略窥门径,这一系列的博文初步都定下来包括SpringBoot介绍、入门、配置、日志相关、web开发、数据访问、结合docker、缓存、消息队列、检索、任务安全、分布式等等一系列的博文,工作量很大,是个漫长的过程,每一步我都尽量详细,配上截图说明,也希望对看的同学真的有用。
单纯就是想分享技术博文,还想说一句就是,如果觉得有用,请点个关注、给个赞吧,也算对我来说是个宽慰,毕竟也得掉不少头发,嘿嘿嘿

系列文章传送条

详细SpringBoot教程之入门(一)
详细SpringBoot教程之入门(二)
详细SpringBoot教程之配置文件(一)
详细SpringBoot教程之配置文件(二)
详细SpringBoot教程之日志框架
详细SpringBoot教程之Web开发(一)
详细SpringBoot教程之Web开发(二)
详细SpringBoot教程之Web开发(三)
详细SpringBoot教程之数据访问
详细SpringBoot教程之启动配置原理
详细SpringBoot教程之缓存开发

启动配置原理

到这里,我们讲解的只是其实已经够我们开发一些比较小的SpringBoot应用了,但是我们随着在使用的过程中,可以对SpringBoot进行底层原理分析,这样我们才能真正将SpringBoot使用的得心应手,然后我们还可以自己开发自己的starter场景启动器,是不是很刺激,后面我们会将,而我这里首先要几个重要的事件回调机制

我们之前也提到过,一般我们的配置核心是在各个配置的META-INF/spring.factories中,而我们SpringBoot的启动相关是

  • ApplicationContextInitializer
  • SpringApplicationRunListener

只需要放在ioc容器中

  • ApplicationRunner
  • CommandLineRunner

启动流程

创建SpringApplication对象

initialize(sources);
private void initialize(Object[] sources) {
    //保存主配置类
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    //判断当前是否一个web应用
    this.webEnvironment = deduceWebEnvironment();
    //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //从多个配置类中找到有main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}

详细SpringBoot教程之启动配置原理插图

详细SpringBoot教程之启动配置原理插图1

运行run方法

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();

   //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
   SpringApplicationRunListeners listeners = getRunListeners(args);
    //回调所有的获取SpringApplicationRunListener.starting()方法
   listeners.starting();
   try {
       //封装命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
            //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成

      Banner printedBanner = printBanner(environment);

       //创建ApplicationContext;决定创建web的ioc还是普通的ioc
      context = createApplicationContext();

      analyzers = new FailureAnalyzers(context);
       //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
  编程电子书汇总     //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
       //回调所有的SpringApplicationRunListener的contextPrepared();
       //
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();

       //s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
       //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
      refreshContext(context);
       //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
       //ApplicationRunner先回调,CommandLineRunner再回调
      afterRefresh(context, applicationArguments);
       //所有的SpringApplicationRunListener回调finished方法
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
       //整个SpringBoot应用启动完成以后返回启动的ioc容器;
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

事件监听机制

配置在META-INF/spring.factories

ApplicationContextInitializer

public class HelloApplicationContextInitializer implements ApplicationContextInitializerConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
    }
}

SpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    //必须有的构造器
    public HelloSpringApplicationRunListener(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);Java面试手册
    }

    @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 finished(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("SpringApplicationRunListener...finished...");
    }
}

配置(META-INF/spring.factories)

org.springframework.context.ApplicationContextInitializer=
com.atguigu.springboot.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=
com.atguigu.springboot.listener.HelloSpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run....");
    }
}

CommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
    }
}

自定义starter

我们在之前的SpringBoot中,引入依赖的starter是不是很舒服,那么我们现在来学着怎么开发自己的starter,其实也非常简单,具体操作如下,首先我们使用Idea创建一个空项目
详细SpringBoot教程之启动配置原理插图2
然后我们会到这里的界面,在这里,我们可以给空项目创建模块,我们如果要自己的启动器的话,那么最基本需要两个模块,不过是哪两个模块呢?这里我们要来讲讲。
详细SpringBoot教程之启动配置原理插图3

我们需要搞清楚两个问题,如下

  • 这个场景需要使用到的依赖是什么?

  • 如何编写自动配置

我们找个starter来进行分析,如下

@Configuration  //指定这个类是一个配置类
@ConditionalOnXXX  //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter  //指定自动配置类的顺序
@Bean  //给容器中添加组件

@ConfigurationPropertie  //结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中

//自动配置类要能加载
//将需要启动就加载的自动配置类,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,

现在我们

所以我们应该符合以下模式:

  • 启动器只用来做依赖导入;

  • 专门来写一个自动配置模块;

  • 启动器依赖自动配置;别人只需要引入启动器(starter)

  • 像mybatis-spring-boot-starter,我们命名应该自定义启动器名-spring-boot-starter

详细SpringBoot教程之启动配置原理插图4
详细SpringBoot教程之启动配置原理插图5
又到了我们熟悉的界面,我们要创建两个,一个是啥都不勾选的SpringBoot项目,还有一个只勾选web模块,创建好了之后,在pom.xml中进行如下更改

启动器模块


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.0modelVersion>

    groupId>com.atguigu.startergroupId>
    artifactId>atguigu-spring-boot-starterartifactId>
    version>1.0-SNAPSHOTversion>

    
    dependencies>

        
        dependency>
            groupId>com.atguigu.startergroupId>
            artifactId>atguigu-spring-boot-starter-autoconfigurerartifactId>
            version>0.0.1-SNAPSHOTversion>
        dependency>
    dependencies>

project>

自动配置模块


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.0modelVersion>

   groupId>com.atguigu.startergroupId>
   artifactId>atguigu-spring-boot-starter-autoconfigurerartifactId>
   version>0.0.1-SNAPSHOTversion>
   packaging>jarpackaging>

   name>atguigu-spring-boot-starter-autoconfigurername>
   description>Demo project for Spring Bootdescription>

   parent>
      groupId>org.springframework.bootgroupId>
      artifactId>spring-boot-starter-parentartifactId>
      version>1.5.10.RELEASEversion>
      relativePath/> 
   parent>

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

   dependencies>

      
      dependency>
         groupId>org.springframework.bootgroupId>
         artifactId>spring-boot-starterartifactId>
      dependency>

   dependencies>

project>

package com.atguigu.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "atguigu.hello")
public class HelloProperties {

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

package com.atguigu.starter;

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloP编程电子书汇总roperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHellAtguigu(String name){
        return helloProperties.getPrefix()+"-" +name + helloProperties.getSuffix();
    }
}

package com.atguigu.starter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.aJava面试手册nnotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication //web应用才生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;
    @Bean
    public HelloService helloService()编程电子书汇总{
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }
}

下一篇

SpringBoot官网其实给了很多案例给我们参考,集成了很多模块的示例代码,SpringBoot放在GitHub上可以下载运行然后研究,下一遍对缓存进行讲解。

> 时间不一定能证明很多东西,但是一定能看透很多东西。坚信自己的选择,不动摇,使劲跑,明天会更好。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » 详细SpringBoot教程之启动配置原理

一个分享Java & Python知识的社区