springboot启动原理

这个故事还是要从启动类开始

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

一层一层点开

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

到这里,先看new SpringApplication(primarySources)做了什么:

1. new SpringApplication

public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

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.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();
    }
  1. primarySources就是我们的主类了,首先检查主类不能为空

  2. this.webApplicationType = WebApplicationType.deduceFromClasspath();判断当前应用类型我们一般都是原生的SERVLET类型

  3. this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    这个方法最总会调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,这个方法在上一篇博客已经写过,会加载所有"META-INF/spring.factories"的文件路径并解析出来不清楚的点这里
    ,然后在根据传入的type.getName()作为key筛选出对应的信息

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

    然后赋值给initializers

  4. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); 像第三步一样找出所有的listener赋值给listeners

  5. this.mainApplicationClass = this.deduceMainApplicationClass();
    从堆栈中有main方法的类作为主程序类

    private Class<?> deduceMainApplicationClass() {
            try {
                StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
                StackTraceElement[] var2 = stackTrace;
                int var3 = stackTrace.length;
    
                for(int var4 = 0; var4 < var3; ++var4) {
                    StackTraceElement stackTraceElement = var2[var4];
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            } catch (ClassNotFoundException var6) {
            }
    
            return null;
        }
    

至此,构造方法已经梳理完毕,是时候看真正的run方法了

2 run方法详解

public ConfigurableApplicationContext run(String... args) {
	// 此类通常用于监控开发过程中的性能,而不是生产应用程序的一部分。
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        // 设置java.awt.headless系统属性,默认为true 
    // Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
        this.configureHeadlessProperty();
        //  1 - 获取SpringApplicationRunListeners
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 通知监听者,开始启动
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 2 - 根据SpringApplicationRunListeners以及参数来准备环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
             // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
            Banner printedBanner = this.printBanner(environment);
            //3 - 创建Spring上下文
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //  4 - Spring上下文前置处理
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //  5 - Spring上下文刷新
            this.refreshContext(context);
            //  6 - Spring上下文后置处理
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			 // 通知监听者,启动完毕
            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
         // 通知监听者,运行中
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
         // 通知监听者,运行失败
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

这个run方法包含的内容有点多的,根据上面列举出的关键步骤逐个进行分析:

2.1 获取RunListeners

SpringApplicationRunListeners listeners = 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));
    }

这个方法已经不陌生了,从 META-INF/spring.factories 中读取Key为 org.springframework.boot.SpringApplicationRunListener 的Values,比如在spring-boot包中的定义的spring.factories:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

我们来看看这个EventPublishingRunListener是干嘛的:

/**
 * {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
 * <p>
 * Uses an internal {@link ApplicationEventMulticaster} for the events that are fired
 * before the context is actually refreshed.
 *
 * @author Phillip Webb
 * @author Stephane Nicoll
 */
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
  // ...
}

从类文档可以看出,它主要是负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理。下面是接口方法,如果上面的代码有细心看,应该不会陌生:每一个方法都会在run里面被调用过

public interface SpringApplicationRunListener {
    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}

2.2 触发所有listeners的starting方法

public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

以LoggingApplicationListener为例,触发其onApplicationEvent方法

public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationStartingEvent) {
			onApplicationStartingEvent((ApplicationStartingEvent) event);
		}
		else if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		else if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
		else if (event instanceof ContextClosedEvent
				&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
			onContextClosedEvent();
		}
		else if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}

onApplicationStartingEvent方法会对日志系统做初始化

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
		this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
		this.loggingSystem.beforeInitialize();
	}

2.3 准备Environment环境

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

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, 
ApplicationArguments applicationArguments) {
	 创建环境,根据webApplicationType创建不同实例
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        // 非Web环境处理
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

配置环境的方法:

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService)conversionService);
        }

        this.configurePropertySources(environment, args);
        this.configureProfiles(environment, args);
    }

所以这里实际上包含了两个步骤:

1.配置 Property Sources
2.配置 Profiles,为应用程序环境配置哪些配置文件处于active(活动)状态。

对于Web应用而言,得到的environment变量是一个StandardServletEnvironment的实例。得到实例后,会调用前面RunListeners中的environmentPrepared方法:

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
            this.application, this.args, environment));
}

在这里,定义的广播器就派上用场了,它会发布一个 ApplicationEnvironmentPreparedEvent 事件

那么有发布就有监听,在构建 SpringApplication 实例的时候不是初始化过一些 ApplicationListeners 嘛,其中的Listener就可能会监听ApplicationEnvironmentPreparedEvent事件,然后进行相应处理。

所以这里 SpringApplicationRunListeners 的用途和目的也比较明显了,它实际上是一个事件中转器,它能够感知到Spring Boot启动过程中产生的事件,然后有选择性的将事件进行中转

那么既然有事件的转发,是谁在监听这些事件呢,在这个类的构造器中交待了:

public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        Iterator var3 = application.getListeners().iterator();

        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            this.initialMulticaster.addApplicationListener(listener);
        }

    }

前面在构建 SpringApplication 实例过程中设置的监听器在这里(SpringApplicationRunListeners listeners = this.getRunListeners(args);)被逐个添加到了 initialMulticaster 对应的 ApplicationListener 列表中。所以当 initialMulticaster 调用 multicastEvent 方法时,这些 Listeners 中定义的相应方法就会被触发了。

2.4 创建Spring Context

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

根据项目类型创建容器,对于servlet类型创建AnnotationConfigServletWebServerApplicationContext

2.5 Spring Context前置处理

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 将环境和上下文关联起来
        context.setEnvironment(environment);
        // 为上下文配置Bean生成器以及资源加载器(如果它们非空)
        this.postProcessApplicationContext(context);
        // 调用初始化器
        this.applyInitializers(context);
        // 触发Spring Boot启动过程的contextPrepared事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
		// 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象// 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
         // 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载 // 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载
        this.load(context, sources.toArray(new Object[0]));
        // 触发Spring Boot启动过程的contextLoaded事件
        listeners.contextLoaded(context);
    }

关键步骤:

  1. 配置Bean生成器以及资源加载器(如果它们非空):

    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
            if (this.beanNameGenerator != null) {
                context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);
            }
    
            if (this.resourceLoader != null) {
                if (context instanceof GenericApplicationContext) {
                    ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);
                }
    
                if (context instanceof DefaultResourceLoader) {
                    ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());
                }
            }
    
            if (this.addConversionService) {
                context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
            }
    
        }
    
  2. 调用初始化器

    protected void applyInitializers(ConfigurableApplicationContext context) {
            Iterator var2 = this.getInitializers().iterator();
    
            while(var2.hasNext()) {
                ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
                Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
                Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
                initializer.initialize(context);
            }
    
        }
    

    这里终于用到了在创建 SpringApplication实例 时设置的初始化器了,依次对它们进行遍历,并调用initialize方法。

  3. 遍历所有listeners调用contextPrepared()方法

     public void contextPrepared(ConfigurableApplicationContext context) {
            Iterator var2 = this.listeners.iterator();
    
            while(var2.hasNext()) {
                SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
                listener.contextPrepared(context);
            }
    
        }
    
  4. 遍历所有listeners调用contextLoaded()方法

    public void contextLoaded(ConfigurableApplicationContext context) {
            Iterator var2 = this.listeners.iterator();
    
            while(var2.hasNext()) {
                SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
                listener.contextLoaded(context);
            }
    
        }
    

2.6 Spring Context刷新

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

    }

调用父类一系列的refresh操作,涉及到了很多核心操作,因此耗时会比较长,参照spring启动流程

protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext)applicationContext).refresh();
    }

注册一个关闭容器时的钩子函数

public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread() {
                public void run() {
                    synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
                        AbstractApplicationContext.this.doClose();
                    }
                }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }

    }

如果没有提供自定义的shutdownHook,那么会生成一个默认的,并添加到Runtime中。默认行为就是调用它的doClose方法,完成一些容器销毁时的清理工作。

2.7 Spring Context后置处理

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

2.8 触发所有listeners的startted方法

public void started(ConfigurableApplicationContext context) {
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.started(context);
        }

    }

2.9 调用callRunners方法

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

    }

获取所有的ApplicationRunner和CommandLineRunner,合并所有runner并排序,遍历调用run方法

2.10 遍历调用所有listeners的running方法

public void running(ConfigurableApplicationContext context) {
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.running(context);
        }

    }

2.11 如果出现异常,遍历调用所有listeners的方法failed方法并关闭容器**

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
        try {
            try {
                this.handleExitCode(context, exception);
                if (listeners != null) {
                    listeners.failed(context, exception);
                }
            } finally {
                this.reportFailure(exceptionReporters, exception);
                if (context != null) {
                    context.close();
                }

            }
        } catch (Exception var9) {
            logger.warn("Unable to close ApplicationContext", var9);
        }

        ReflectionUtils.rethrowRuntimeException(exception);
    }

至此,springBoot启动完毕
推荐阅读spring (五)容器创建总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值