springboot 启动流程

springboot 启动源码解析

注: 以下源码是基于: 2.2.6.RELEASE 版本做的摘录分析

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starters</artifactId>
    <version>2.2.6.RELEASE</version>
  </parent>
   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
   </dependencies>   

大致类图:

@SpringBootApplication
public class CollmallProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(CollmallProductApplication.class, args);
    }
}

public class SpringApplication {
    
     private boolean logStartupInfo;
     private boolean headless;
     private Banner banner;
     private ResourceLoader resourceLoader;
    
    // 构造器 1
     public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }
    // 构造器 2
    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));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    
    // 此处省略很多行代码  
    
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }
    
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
    
  public ConfigurableApplicationContext run(String... args) {
        // 1. 创建并启动计时监控类:此计时器是为了监控并记录 Spring Boot 应用启动的时间的,它会记录当前任务的名称,然后开启计时器。
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 2. 声明应用上下文和异常报告集合: 此过程声明了应用上下文对象和一个异常报告的 ArrayList 集合。
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        // 3. 设置系统属性 headless的值:设置 Java.awt.headless = true,其中 awt(Abstract Window Toolkit)的含义是抽象窗口工具集。设置为 true 表示运行一个 headless 服务器,可以用它来作一些简单的图像处理。
        this.configureHeadlessProperty();
        // 4. 创建所有 Spring 运行监听器并发布应用启动事件:此过程用于获取配置的监听器名称并实例化所有的类。
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 5. 处理args 参数:也就是说声明并创建一个应用参数对象。
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 6. 配置环境: 创建配置并且绑定环境(通过 property sources 和 profiles 等配置文件)。
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 7. 创建Banner 的打印类: 此 banner 信息是在 SpringBootBanner 类中定义的,可以通过实现 Banner 接口来自定义 banner 信息,然后通过代码 setBanner() 方法设置 Spring Boot 项目使用自己自定义 Banner 信息,或者是在 resources 下添加一个 banner.txt,把 banner 信息添加到此文件中,就可以实现自定义 banner 的功能了。
            Banner printedBanner = this.printBanner(environment);
            // 8. 创建应用上下文:根据不同的应用类型来创建不同的 ApplicationContext 上下文对象。
            context = this.createApplicationContext();
            // 9. 实例化异常报告器:它调用的是 getSpringFactoriesInstances() 方法来获取配置异常类的名称,并实例化所有的异常处理类。
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 10. 准备应用上下文:此方法的主要作用是把上面已经创建好的对象,传递给 prepareContext 来准备上下文,例如将环境变量 environment 对象绑定到上下文中、配置 bean 生成器以及资源加载器、记录启动日志等操作。
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 11. 刷新应用上下文:此方法用于解析配置文件,加载 bean 对象,并且启动内置的 web 容器等操作。
            this.refreshContext(context);
            // 12. 应用上下文刷新之后的事件处理:这个方法的源码是空的,可以做一些自定义的后置处理操作。
            this.afterRefresh(context, applicationArguments);
            // 13. 停止计时监控类:停止此过程第一步中的程序计时器,并统计任务的执行信息。
            stopWatch.stop();
            // 14. 输入日志记录执行主类名, 时间信息 :把相关的记录信息,如类名、时间等信息进行控制台输出。
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
            // 15. 发布应用上下文启动完成事件:触发所有 SpringApplicationRunListener 监听器的 started 事件方法。
            listeners.started(context);
            // 16. 执行所有Runner 运行器:执行所有的 ApplicationRunner 和 CommandLineRunner 运行器。
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            // 17. 发布应用上下文就绪事件:触发所有的 SpringApplicationRunListener 监听器的 running 事件。
            listeners.running(context);
            // 18 .返回上下文对象
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

// ===============================================================================================
// 以下是主要方法的实现    
private void configureHeadlessProperty() {
        System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
}
  
private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger,      this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}  
    
private Banner printBanner(ConfigurableEnvironment environment) {
        if (this.bannerMode == Mode.OFF) {
            return null;
        } else {
            ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(this.getClassLoader());
            SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);
            return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);
        }
} 
  
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
  }
  // 获取springFactories的实例  
  private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
}
    
 public ClassLoader getClassLoader() {
        return this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader();
}
    
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        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]));
        listeners.contextLoaded(context);
}
    
private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
                ;
            }
        }
}
    
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
   
void started(ConfigurableApplicationContext context) {
        Iterator var2 = this.listeners.iterator();
        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.started(context);
        }

 }
    
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);
            }
        }
}
    
void running(ConfigurableApplicationContext context) {
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.running(context);
        }
    }
}
@FunctionalInterface
public interface Banner {
    void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

    public static enum Mode {
        OFF,
        CONSOLE,
        LOG;

        private Mode() {
        }
    }
}

到此为止 Spring Boot 的启动程序就结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇凝子潇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值