Springboot 2.X启动源码分析

@SpringBootApplication
@EnableFeignClients(defaultConfiguration = FeignClientsConfiguration.class)
public class DocApplication {

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

}

从run这个方法点击 进入源码可以发现最终调用的方法为 SpringApplication.run(String...args)

 public ConfigurableApplicationContext run(String... args) {//String... args 是可变参数 (jdk1.5之后的新特性) 类似 String[] args
        // 1、创建并启动计时监控类
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 2、初始化应用上下文和异常报告集合
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
         // 3、设置系统属性 `java.awt.headless` 的值,默认值为:true        
        this.configureHeadlessProperty();
        // 4、创建所有 Spring 运行监听器并发布应用启动事
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
             // 5、初始化默认应用参数类
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
             // 6、根据运行监听器和应用参数来准备 Spring 环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
             // 7、创建 Banner 打印类
            Banner printedBanner = this.printBanner(environment);
             // 8、创建应用上下文
            context = this.createApplicationContext();
             // 9、准备异常报告器
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
              // 10、准备应用上下文
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
             // 11、刷新应用上下文
            this.refreshContext(context);
            // 12、应用上下文刷新后置处理
            this.afterRefresh(context, applicationArguments);
            // 13、停止计时监控类
            stopWatch.stop();
            // 14、输出日志记录执行主类名、时间信息
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
             // 15、发布应用上下文启动完成事件
            listeners.started(context);
             // 16、执行所有 Runner 运行器
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
             // 17、发布应用上下文就绪事件
            listeners.running(context);
            // 18、返回应用上下文
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

主要根据这个方法来分析启动过程

1、创建并启动计时器监控类StopWatch

  StopWatch stopWatch = new StopWatch();
        stopWatch.start();

StopWatch start部分的源码

 public void start() throws IllegalStateException {
        this.start("");
    }

    public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
            throw new IllegalStateException("Can't start StopWatch: it's already running");
        } else {
            this.currentTaskName = taskName;//记录任务名称
            this.startTimeMillis = System.currentTimeMillis();//记录任务开始时间
        }
    }

2、初始化应用上下文和异常报告集合

源码如下 没什么讲

        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();

 3、设置系统属性java.awt.headless的值

this.configureHeadlessProperty();
 private void configureHeadlessProperty() {
        System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));//headless默认值为true
    }

问题  Java.awt.headless = true 有什么作用?

对于一个 Java 服务器来说经常要处理一些图形元素,例如地图的创建或者图形和图表等。这些API基本上总是需要运行一个X-server以便能使用AWT(Abstract Window Toolkit,抽象窗口工具集)。然而运行一个不必要的 X-server 并不是一种好的管理方式。有时你甚至不能运行 X-server,因此最好的方案是运行 headless 服务器,来进行简单的图像处理。

4、创建所有Spring运行监听器并发布应用启动事件

a、

SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();//找到匹配的监听器,广播事件,开始监听

a.1、首先是this.getRunListeners(args)的源码 

 private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

a.1.1、先看 this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)

 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//动态加载jar、资源文件 的线程上下文类加载器
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//方法获取spring-boot-2.0.6.RELEASE.jar!/META-INF/spring.factories配置文件里面所有的监听器名称
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);//实例化 上面得到的名称集合
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
a.1.1.1、其中  SpringFactoriesLoader.loadFactoryNames(type, classLoader)) 中的部分源码
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

从中可以看出  loadSpringFactories方法直接加载所有的META-INF/spring.factories文件内容,其内部还是调用PropertiesLoaderUtils.loadProperties方法进行处理。 在此处使用了 缓存策略

a.1.1.2 this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); 的源码 

根据得到的名称集合 实例化所有的监听类

    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList(names.size());
        Iterator var7 = names.iterator();

        while(var7.hasNext()) {
            String name = (String)var7.next();

            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            } catch (Throwable var12) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
            }
        }

        return instances;
    }
5、初始化默认应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

6、根据运行监听器和应用参数来准备 Spring 环境

 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);

具体源码

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        //6.1 获取(或者创建)应用环境
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        // 6.2 配置应用环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        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;
    }

6.1获取(或创建)应用环境

   private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        } else {
            switch(this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
            }
        }
    }

6.1 获取(或者创建)应用环境

   protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        this.configurePropertySources(environment, args);
        this.configureProfiles(environment, args);
    }

这里分为以下两步来配置应用环境。

  • 配置 property sources
  • 配置 Profiles

这里主要处理所有 property sources 配置和 profiles 配置。

7、创建 Banner 打印类

            Banner printedBanner = this.printBanner(environment);

用来打印 Banner 的处理类

8、创建应用上下文

 context = this.createApplicationContext();

接下来 this.createApplicationContext()的源码

    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);
    }
根据不同的应用类型初始化不同的上下文应用类

9、准备异常报告器

            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

逻辑和之前实例化初始化器和监听器的一样,一样调用的是 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。

该异常报告处理类配置在 spring-boot-2.0.6.RELEASE.jar!/META-INF/spring.factories 这个配置文件里面。

10、准备应用上下文

            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

该方法的源码

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 10.1)绑定环境到上下文
        context.setEnvironment(environment);
        // 10.2)配置上下文的 bean 生成器及资源加载器
        this.postProcessApplicationContext(context);
        // 10.3)为上下文应用所有初始化器
        this.applyInitializers(context);
         // 10.4)触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
        listeners.contextPrepared(context);
        // 10.5)记录启动日志
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
        // 10.6)注册两个特殊的单例bean
        context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }
         // 10.7)加载所有资源
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        // 10.8)触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
        listeners.contextLoaded(context);
    }

11、刷新应用上下文

 this.refreshContext(context);

源码如下

 private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

    }

12、应用上下文刷新后置处理

            this.afterRefresh(context, applicationArguments);

该源码方法为空

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    }

13、停止计时监控类

 stopWatch.stop();

计时监听停止,并统计一些任务执行信息

    public void stop() throws IllegalStateException {
        if (this.currentTaskName == null) {
            throw new IllegalStateException("Can't stop StopWatch: it's not running");
        } else {
            long lastTime = System.currentTimeMillis() - this.startTimeMillis;
            this.totalTimeMillis += lastTime;
            this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
            if (this.keepTaskList) {
                this.taskList.add(this.lastTaskInfo);
            }

            ++this.taskCount;
            this.currentTaskName = null;
        }
    }

14、输出日志记录执行主类名、时间信息

  if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

15、发布应用上下文启动完成事件

 listeners.started(context);

16、执行所有 Runner 运行器

 this.callRunners(context, applicationArguments);

源码为

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

    }

执行所有 ApplicationRunnerCommandLineRunner 这两种运行器

17、发布应用上下文就绪事件并返回应用上下文

   listeners.running(context);//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。

            return context;

总结

持续学习中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值