SpringBoot启动原理 & 自定义starter

SpringBoot启动原理

启动流程

创建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();
}

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

配置在META-INF/spring.factories (在resources目录下创建META-INF,将spring.factoties文件放到该目录下)

ApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @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);
    }

    @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容器

下面几个不用再sping.factories中注册,只需要将自己定义的Runner放在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

自定义starter需要注意的两个问题

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

  • 如何编写自动配置

结合SpringBoot自动配置的经验大致要做的事情有下面这些:

@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

命名规则

推荐使用以下命名规则:

  • 官方命名
    • 前缀:”spring-boot-starter-”

    • 模式:spring-boot-starter-模块名

    • 举例:spring-boot-starter-webspring-boot-starter-jdbc

  • 自定义命名
    • 后缀:”-spring-boot-starter”

    • 模式:模块名-spring-boot-starter

    • 举例:mybatis-spring-boot-starter

自定义starter

首先建立两个项目,

一个做启动器,什么都不写,只导入关于配置组件的依赖

另一个项目做配置模块

启动器

只需要在第一个项目中的pom.xml文件中引入配置组件的依赖即可,同时删掉buildtest依赖。其他的任何操作都不需要

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


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

</project>

配置模块

首先在pom.xml中引入springbootstarter,这是所有的starter的基本配置

<dependencies>
    <!-- 引入springboot的starter:所有starter的基本配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

然后创建一个xxxProperties类用来放配置参数

@ConfigurationProperties(prefix = "jinxin.hello")
public class HelloProperties {
    private String prefix;
    private String surfix;


    public String getPrefix() {
        return prefix;
    }

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

    public String getSurfix() {
        return surfix;
    }

    public void setSurfix(String surfix) {
        this.surfix = surfix;
    }
}

然后创建xxxAutoConfiguration用作自动配置类,并自动配置刚刚的xxxProperties类,并给xxxService设置上导入的xxxProperties

@Configuration
@ConditionalOnWebApplication     // web应用才生效
@EnableConfigurationProperties(HelloProperties.class)   // 让这个属性文件生效
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;

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

创建xxxService类用作业务实现类

public class HelloService {
    HelloProperties helloProperties;


    public String sayHello(String name){
        return helloProperties.getPrefix() + "-" + name + "-" + helloProperties.getSurfix();
    }


    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

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

resources下创建META-INF目录,并创建spring.factories,并配置刚刚的xxxAutoConfiguration

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.jinxin.starter.HelloServiceAutoConfiguration

这样一个starter就配置好了,然后将这两个包安装(Install)Maven仓库即可

测试starter

重新创建一个项目,导入刚刚的starter的依赖

<!-- 引入自定义的starter -->
<dependency>
    <groupId>com.jinxin.starter</groupId>
    <artifactId>jinxin-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

使用starter中的xxxService

@RestController
public class HelloController {
    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.sayHello("hahahaha");
    }
}

在配置文件中设置xxxProperties的参数

jinxin.hello.prefix=Jinxin
jinxin.hello.surfix=HELLO WORLD

 

posted @ 2018-09-15 10:45  Jin同学  阅读(1103)  评论(0编辑  收藏  举报