SpringBoot_day_09

Spring Boot启动配置原理:启动原理、运行流程、自动配置原理。

几个重要的事件回调机制

配置在META-INF/spring.factories

ApplicationContextInitializer

SpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

CommandLineRunner


启动流程:

首先创建一个web的Springboot项目:

然后给主main方法打上断点。

开始debug运行。

创建SpringApplication对象



    private void initialize(Object[] sources) {
        //保存主配置类
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
        //判断当前是否一个web应用
        this.webEnvironment = this.deduceWebEnvironment();

        //从类路径下找到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();
    }

6个ApplicationContextInitializer:

10个ApplicationListener:

运行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);

           //准备环境:创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
           ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

           //打印Spring的banner
           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()

           //刷新容器;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);
        }
    }

事件监听机制

上面我们已经了解了SpringBoot的启动原理,重点是里面的几个监听器:

ApplicationContextInitializer、SpringApplicationRunListener、ApplicationRunner、CommandLineRunner

现在,我们来自定义这几个监听器:

在根目录包下,创建listener包,并创建这几个接口的实现类:

ApplicationContextInitializer:

public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("MyApplicationContextInitializer---initialize" + configurableApplicationContext);

    }
}

SpringApplicationRunListener:

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

ApplicationRunner:

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..." + Arrays.asList(args));
    }
}


要想使以上的监听器起作用:

ApplicationContextInitializer 和 SpringApplicationRunListener需要配置在META-INF/spring.factories

在resources下:创建META-INF包,并创建spring.factories文件:

spring.factories文件:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.liuzhuo.springboot.listener.MyApplicationContextInitializer

# SpringApplicationRunListene Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.liuzhuo.springboot.listener.MySpringApplicationRunListener

如果不会写,随便打开一个SpringBoot的jar包,打开一个META-INF/spring.factories,照着写就行了。

ApplicationRunner 和 CommandLineRunner 只需要放到IOC容器中就行,即加上@Component即可。


启动SpringBoot应用:

出现异常:

MySpringApplicationRunListener.<init>(org.springframework.boot.SpringApplication, [Ljava.lang.String;)

说明SpringApplicationRunListener,需要有一个有参的构造函数,第一个参数是SpringApplication,第二个是String类型。

不会写的话,随便打开一个SpringApplicationRunListener的实现类:

双击:SpringApplicationRunListener,然后Ctrl + H :

点击:EventPublishingRunListener

现在给我们的MySpringApplicationRunListener,添加有参的构造函数:

再次启动SpringBoot应用:

自定义starter

starter:

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

2、如何编写自动配置

@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,\

3、模式:

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

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

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

mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter

步骤:

前提准备:

创建一个空项目:

添加我们的模块:

1) 创建启动器模块:

选择Maven项目:



2)创建自动配置模块

选择Spring Initializr:(只是为了方便快速创建模块)


选择web模块:


3)在启动器pom文件中,引入自动配置的依赖:

<?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.liuzhuo.spring</groupId>
    <artifactId>liuzhuo.spring.boot.starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--启动器-->
    <dependencies>

        <!--引入自动配置模块-->
        <dependency>
            <groupId>com.liuzhuo.starter</groupId>
            <artifactId>liuzhuo-spring-boot-starter-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

    </dependencies>


</project>

4) 编写自动配置模块:

删除不需要的文件:

pom文件:删除test的依赖 和 插件的依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

启动类也删除:
最终:

在starter包下:创建HelloService

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

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

}

在starter包下:创建HelloProperties:

@ConfigurationProperties(prefix = "liuzhuo.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;
    }
}

在starter包下:创建HelloServiceAutoConfiguration:

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

    @Autowired
    private HelloProperties helloProperties;

    @Bean
    public HelloService helloService() {
        HelloService helloService = new HelloService();
        helloService.setHelloProperties(helloProperties);
        return helloService;
    }
}

在resources下:创建META-INF/spring.factories文件

spring.factories:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.liuzhuo.starter.HelloServiceAutoConfiguration

5) 将这两个模块,导入到本地maven仓库中:

首先导入liuzhuo-spring-boot-starter-autoconfigure的依赖

因为liuzhuo-spring-boot-starter依赖于liuzhuo-spring-boot-starter-autoconfigure。

打开maven的工具窗口,点击liuzhuo-spring-boot-starter-autoconfigure模块,找到生命周期中的install。双击

liuzhuo-spring-boot-starter的导入本地仓库,类似。

6)创建新的SpringBoot的web项目,导入我们的liuzhuo-spring-boot-starter依赖,测试。

导入:liuzhuo-spring-boot-starter依赖

        <!--导入我们的启动依赖-->
        <dependency>
            <groupId>com.liuzhuo.starter</groupId>
            <artifactId>liuzhuo.spring.boot.starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

创建:HelloController

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.sayHelloLiuzhuo("jackLoveDj");
    }
}

在配置文件中:

liuzhuo.hello.prefix=欢迎
liuzhuo.hello.suffix=到来

启动我们的应用:

没有报错,说明我们的自动配置成功了。

在浏览器中输入:http://localhost:8080/hello


更多SpringBoot整合示例

官方给出的样例代码


  转载请注明: 解忧杂货店 SpringBoot_day_09

  目录