简单读!springboot 启动过程概览

  springboot 说是没有新技术,但是刚开始用的时候,总有许多疑问,它是怎么做到的?让我们通过源码来解释吧!

  让我们来看一个springboot项目的启动过程吧!

启动类,即一个main入口:

复制代码
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableTransactionManagement
@MapperScan("com.xxx.dao")
@EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class})
@ComponentScan(basePackages = "com.xxx.**,")
public class StartApplication {
    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class, args);
    }
}
复制代码

  运行该方法后,springboot服务就起来了!

  其作用就是,通过注解加入一些配置,然后交由SpringApplication.run()进行操作,因此,可以认为,springboot的启动方法为 SpringApplication.run();

复制代码
    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified source using default settings.
     * @param primarySource the primary source to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }

    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     * @param primarySources the primary sources to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
复制代码

 

  SpringApplication.run()做了两件事:

    1. new一个 SpringApplication 实例,将启动类信息传递进去;

    2. 调用SpringApplication的实例方法run(),注意,这个run与前面的run() 是不一样的,前端的run()是静态方法,是为了方便外部调用,而实例的run()才是做实事的!

我们先看实例化 SpringApplication 怎么样的?

  

复制代码
    // org.springframework.boot.SpringApplication
    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }

    /**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified primary sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param resourceLoader the resource loader to use
     * @param primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #setSources(Set)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = deduceWebApplicationType();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
复制代码

  想来该实例化过程应该不会太简单,毕竟整个应用的启动都需要交给它来承担了。其主要做这几件事:

    1. 将启动类设值到实例变量中,以备其他用途;

    2. 推断出当前启动的应用类型,有:WebApplicationType.REACTIVE/NONE/SERVLET 三种模式,用于后续进行创建应用引擎;

    3. 设置 ApplicationContextInitializer.class 的初始化相关类;

    4. 设置 ApplicationListener.class 的相关监听类;

    5. 推断出入口类,并实例化一个入口类,即: StartApplication;(注意启动的时候入口类并没有进行实例化)

  其中,获取的 initializer / listeners 主要有:

复制代码
Initializers:
 - org.springframework.boot.context.config.DelegatingApplicationContextInitializer
 - org.springframework.boot.context.ContextIdApplicationContextInitializer
 - org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
 - org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
 - org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
 - org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
 
Listeners:
 - org.springframework.cloud.bootstrap.BootstrapApplicationListener
 - org.springframework.cloud.bootstrap.LoggingSystemShutdownListener
 - org.springframework.boot.context.config.ConfigFileApplicationListener
 - org.springframework.boot.context.config.AnsiOutputApplicationListener
 - org.springframework.boot.context.logging.LoggingApplicationListener
 - org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
 - org.springframework.boot.autoconfigure.BackgroundPreinitializer
 - org.springframework.boot.context.config.DelegatingApplicationListener
 - org.springframework.cloud.context.restart.RestartListener
 - org.springframework.boot.builder.ParentContextCloserApplicationListener
 - org.springframework.boot.ClearCachesApplicationListener
 - org.springframework.boot.context.FileEncodingApplicationListener
 - org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

 BootstrapConfiguration.class
 - org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration
 - org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration
 - org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration
 - org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
 - org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration
 - org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration
复制代码

  其中一些启动监听时的栈桢如下:

复制代码
      
"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
      at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:207)
      at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:104)
      at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:70)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
      at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)
      at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
      at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:364)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265)
      at com.xxx.service.StartApplication.main(StartApplication.java:26)
复制代码

  BootstrapApplicationListener 则会对整个应用进程进行启动,它会再次回来调用 SpringApplication, 进行应用刷新动作:

复制代码
"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
      at org.springframework.boot.builder.SpringApplicationBuilder.createSpringApplication(SpringApplicationBuilder.java:102)
      at org.springframework.boot.builder.SpringApplicationBuilder.<init>(SpringApplicationBuilder.java:90)
      at org.springframework.cloud.stream.binder.DefaultBinderFactory.getBinderInstance(DefaultBinderFactory.java:187)
      at org.springframework.cloud.stream.binder.DefaultBinderFactory.getBinder(DefaultBinderFactory.java:139)
      - locked <0x34b5> (a org.springframework.cloud.stream.binder.DefaultBinderFactory)
      at org.springframework.cloud.stream.binding.BindingService.getBinder(BindingService.java:308)
      at org.springframework.cloud.stream.binding.BindingService.bindProducer(BindingService.java:208)
      at org.springframework.cloud.stream.binding.BindableProxyFactory.bindOutputs(BindableProxyFactory.java:252)
      at org.springframework.cloud.stream.binding.OutputBindingLifecycle.doStartWithBindable(OutputBindingLifecycle.java:46)
      at org.springframework.cloud.stream.binding.AbstractBindingLifecycle$$Lambda$726.576149775.accept(Unknown Source:-1)
      at java.util.Iterator.forEachRemaining(Iterator.java:116)
      at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
      at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
      at org.springframework.cloud.stream.binding.AbstractBindingLifecycle.start(AbstractBindingLifecycle.java:47)
      at org.springframework.cloud.stream.binding.OutputBindingLifecycle.start(OutputBindingLifecycle.java:29)
      at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182)
      at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53)
      at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360)
      at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158)
      at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122)
      at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:885)
      at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
      - locked <0x34df> (a java.lang.Object)
      at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
      at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
      at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265)
      at com.xxx.service.StartApplication.main(StartApplication.java:26)
复制代码

  而对于经典的spring架构,都会进行一个 refresh() 的操作,在spring中则体现为:

复制代码
# refresh
"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
      at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:891)
      at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
      - locked <0x34df> (a java.lang.Object)
      at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
      at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
      at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265)
      at com.xxx.service.StartApplication.main(StartApplication.java:26)
复制代码

 

  SpringApplication.run() 方法,其体现的是整体架构过程:

复制代码
    /**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
复制代码

  步骤已经很清晰:

    1. 配置 headless 属性信息;

    2. 开启 listeners.starting(),为后续事件做准备;

    3. 环境准备,bootstrap,application...; printBanner()的作用是打印[SpringBoot]字样的logo作用;
    4. 创建 ConfigurableApplicationContext 应用上下文;
    5. 准备上下文需要的信息,设值,hook...
    6. 刷新上下文 refreshContext(); 重点初始化在此;
    7. 刷新后续钩子处理;
    8. 监听器监听started方法 listeners.started();
    9. callRunners 回调;
    10. 监听器监听 running方法, listeners.running();
    11. 其他,整个项目在运行期间,大量利用监听器 listeners 的监听,进行注册各自的服务;

  我们主要来看一下springboot的事件监听和响应过程,把这个过程明白了,整个springboot也就清晰了:

复制代码
    // org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(), 监听生命周期事件
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }
    
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
    
    /**
     * Invoke the given listener with the given event.
     * @param listener the ApplicationListener to invoke
     * @param event the current event to propagate
     * @since 4.1
     */
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            // call
            doInvokeListener(listener, event);
        }
    }
    // doInvokeListener
    @SuppressWarnings({"unchecked", "rawtypes"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let's suppress the exception and just log a debug message.
                Log logger = LogFactory.getLog(getClass());
                if (logger.isDebugEnabled()) {
                    logger.debug("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }
复制代码

  可以看到,其实事件监听入口只有一个,不过到下面的时候,都会重新去获取一次监听者列表,进行各自通知,从而独立加载各组件!

  监听器可能有如下几个:

复制代码
  - org.springframework.cloud.bootstrap.BootstrapApplicationListener
  - org.springframework.cloud.bootstrap.LoggingSystemShutdownListener
  - org.springframework.boot.context.config.ConfigFileApplicationListener
  - org.springframework.boot.context.config.AnsiOutputApplicationListener
  - org.springframework.boot.context.logging.LoggingApplicationListener
  - org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
  - org.springframework.boot.autoconfigure.BackgroundPreinitializer
  - org.springframework.boot.context.config.DelegatingApplicationListener
  - org.springframework.boot.context.FileEncodingApplicationListener
复制代码

  其中,重要的监听器: BootstrapApplicationListener, 会触发应用的启动动作;其监听 onApplicationEvent() 如下:

复制代码
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
                true)) {
            return;
        }
        // don't listen to events in a bootstrap context
        if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
            return;
        }
        ConfigurableApplicationContext context = null;
        String configName = environment
                .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
        for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
                .getInitializers()) {
            if (initializer instanceof ParentContextApplicationContextInitializer) {
                context = findBootstrapContext(
                        (ParentContextApplicationContextInitializer) initializer,
                        configName);
            }
        }
        if (context == null) {
            // 如果是第一次访问,就会走初始化动作
            context = bootstrapServiceContext(environment, event.getSpringApplication(),
                    configName);
        }
        apply(context, event.getSpringApplication(), environment);
    }
复制代码

   如上的 BootstrapApplicationListener.onApplicationEvent() 主要做的事:

    1. 检测是否启用配置参数 spring.cloud.bootstrap.enabled, 是否有 bootstrap 的资源(如果有的话意味着当前正在或已经加载完成bootstrp),从而决定是否监听事件;
    2. 根据 ${spring.cloud.bootstrap.name:bootstrap} 配置,查找 bootstrapContext;
    3. 如果没有找到 bootstrapContext; 则进行初始化;
    4. 将context应用到apply() 中;

复制代码
    // 初始化 bootstrap
    private ConfigurableApplicationContext bootstrapServiceContext(
            ConfigurableEnvironment environment, final SpringApplication application,
            String configName) {
        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
        MutablePropertySources bootstrapProperties = bootstrapEnvironment
                .getPropertySources();
        for (PropertySource<?> source : bootstrapProperties) {
            bootstrapProperties.remove(source.getName());
        }
        String configLocation = environment
                .resolvePlaceholders("${spring.cloud.bootstrap.location:}");
        Map<String, Object> bootstrapMap = new HashMap<>();
        bootstrapMap.put("spring.config.name", configName);
        // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail
        // force the environment to use none, because if though it is set below in the builder
        // the environment overrides it
        bootstrapMap.put("spring.main.web-application-type", "none");
        if (StringUtils.hasText(configLocation)) {
            bootstrapMap.put("spring.config.location", configLocation);
        }
        bootstrapProperties.addFirst(
                new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
        for (PropertySource<?> source : environment.getPropertySources()) {
            if (source instanceof StubPropertySource) {
                continue;
            }
            bootstrapProperties.addLast(source);
        }
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
        List<String> names = new ArrayList<>(SpringFactoriesLoader
                .loadFactoryNames(BootstrapConfiguration.class, classLoader));
        for (String name : StringUtils.commaDelimitedListToStringArray(
                environment.getProperty("spring.cloud.bootstrap.sources", ""))) {
            names.add(name);
        }
        // TODO: is it possible or sensible to share a ResourceLoader?
        SpringApplicationBuilder builder = new SpringApplicationBuilder()
                .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
                .environment(bootstrapEnvironment)
                // Don't use the default properties in this builder
                .registerShutdownHook(false).logStartupInfo(false)
                .web(WebApplicationType.NONE);
        if (environment.getPropertySources().contains("refreshArgs")) {
            // If we are doing a context refresh, really we only want to refresh the
            // Environment, and there are some toxic listeners (like the
            // LoggingApplicationListener) that affect global static state, so we need a
            // way to switch those off.
            builder.application()
                    .setListeners(filterListeners(builder.application().getListeners()));
        }
        List<Class<?>> sources = new ArrayList<>();
        for (String name : names) {
            Class<?> cls = ClassUtils.resolveClassName(name, null);
            try {
                cls.getDeclaredAnnotations();
            }
            catch (Exception e) {
                continue;
            }
            sources.add(cls);
        }
        AnnotationAwareOrderComparator.sort(sources);
        builder.sources(sources.toArray(new Class[sources.size()]));
        // 创建 context, 使用 SpringApplicationBuilder 创建, 最终调用到 SpringApplication.run(), 从而形成一个递归
        final ConfigurableApplicationContext context = builder.run();
        // gh-214 using spring.application.name=bootstrap to set the context id via
        // `ContextIdApplicationContextInitializer` prevents apps from getting the actual
        // spring.application.name
        // during the bootstrap phase.
        context.setId("bootstrap");
        // Make the bootstrap context a parent of the app context
        addAncestorInitializer(application, context);
        // It only has properties in it now that we don't want in the parent so remove
        // it (and it will be added back later)
        bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
        mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
        return context;
    }
复制代码

  第二个listener: LoggingSystemShutdownListener, 其作用是注册日志组件,为context打印日志提供能力;其 onApplicationEvent() 响应如下:

复制代码
    //org.springframework.cloud.bootstrap.LoggingSystemShutdownListener
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        shutdownLogging();
    }

    private void shutdownLogging() {
        LoggingSystem loggingSystem = LoggingSystem
                .get(ClassUtils.getDefaultClassLoader());
        loggingSystem.cleanUp();
        loggingSystem.beforeInitialize();
    }
复制代码

  第三个 listener 是 ConfigFileApplicationListener, 在这里处理多配置文件的问题,其 onApplicationEvent() 监听如下:

复制代码
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent(event);
        }
    }
复制代码

  针对 ApplicationEnvironmentPreparedEvent 环境准备事件,触发初始化动作;

复制代码
    private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }
复制代码

  使用的处理器有:

     - org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
     - org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
     - org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
     - org.springframework.cloud.client.HostInfoEnvironmentPostProcessor
     - org.springframework.boot.context.config.ConfigFileApplicationListener
     - org.springframework.cloud.bus.BusEnvironmentPostProcessor

  针对 SystemEnvironmentPropertySourceEnvironmentPostProcessor 会去处理 bootstrap 的引入问题,载入系统的配置,如JAVA_HOME:

复制代码
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
        PropertySource<?> propertySource = environment.getPropertySources()
                .get(sourceName);
        if (propertySource != null) {
            replacePropertySource(environment, sourceName, propertySource);
        }
    }

    @SuppressWarnings("unchecked")
    private void replacePropertySource(ConfigurableEnvironment environment,
            String sourceName, PropertySource<?> propertySource) {
        Map<String, Object> originalSource = (Map<String, Object>) propertySource
                .getSource();
        SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(
                sourceName, originalSource);
        environment.getPropertySources().replace(sourceName, source);
    }
复制代码

  接下来是对 SpringApplicationJsonEnvironmentPostProcessor 的处理过程:

复制代码
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        StreamSupport.stream(propertySources.spliterator(), false)
                .map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst()
                .ifPresent((v) -> processJson(environment, v));
    }

    private void processJson(ConfigurableEnvironment environment,
            JsonPropertyValue propertyValue) {
        try {
            JsonParser parser = JsonParserFactory.getJsonParser();
            Map<String, Object> map = parser.parseMap(propertyValue.getJson());
            if (!map.isEmpty()) {
                addJsonPropertySource(environment,
                        new JsonPropertySource(propertyValue, flatten(map)));
            }
        }
        catch (Exception ex) {
            logger.warn("Cannot parse JSON for spring.application.json: "
                    + propertyValue.getJson(), ex);
        }
    }
复制代码

  接下来是对 CloudFoundryVcapEnvironmentPostProcessor 处事件监听处理,主要是对云上配置的设置,如果不是springCloud,则直接忽略:

复制代码
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
            Properties properties = new Properties();
            addWithPrefix(properties, getPropertiesFromApplication(environment),
                    "vcap.application.");
            addWithPrefix(properties, getPropertiesFromServices(environment),
                    "vcap.services.");
            MutablePropertySources propertySources = environment.getPropertySources();
            if (propertySources.contains(
                    CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
                propertySources.addAfter(
                        CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
                        new PropertiesPropertySource("vcap", properties));
            }
            else {
                propertySources
                        .addFirst(new PropertiesPropertySource("vcap", properties));
            }
        }
    }
复制代码

  接下来是对 HostInfoEnvironmentPostProcessor, 域名的解析,也主要针对cloud环境的验证:

复制代码
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        InetUtils.HostInfo hostInfo = getFirstNonLoopbackHostInfo(environment);
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        map.put("spring.cloud.client.hostname", hostInfo.getHostname());
        map.put("spring.cloud.client.ip-address", hostInfo.getIpAddress());
        MapPropertySource propertySource = new MapPropertySource(
                "springCloudClientHostInfo", map);
        environment.getPropertySources().addLast(propertySource);
    }
复制代码

  接下来是对 ConfigFileApplicationListener 的监听:

复制代码
    
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        addPropertySources(environment, application.getResourceLoader());
    }

        // Loader.Loader()
        Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            this.environment = environment;
            this.resourceLoader = (resourceLoader != null) ? resourceLoader
                    : new DefaultResourceLoader();
            // 这里资源加载方式有两种
            // - org.springframework.boot.env.PropertiesPropertySourceLoader
            // - org.springframework.boot.env.YamlPropertySourceLoader
            this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
                    PropertySourceLoader.class, getClass().getClassLoader());
        }

        // 装载具体的配置文件
        public void load() {
            this.profiles = new LinkedList<>();
            this.processedProfiles = new LinkedList<>();
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap<>();
            initializeProfiles();
            // 对 default, dev ... , 进行遍历, 即在此处载入环境配置文件
            while (!this.profiles.isEmpty()) {
                Profile profile = this.profiles.poll();
                if (profile != null && !profile.isDefaultProfile()) {
                    addProfileToEnvironment(profile.getName());
                }
                load(profile, this::getPositiveProfileFilter,
                        addToLoaded(MutablePropertySources::addLast, false));
                this.processedProfiles.add(profile);
            }
            // 重置环境配置
            resetEnvironmentProfiles(this.processedProfiles);
            // 载入配置变量,加载
            load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));
            addLoadedPropertySources();
        }

        /**
         * Initialize profile information from both the {@link Environment} active
         * profiles and any {@code spring.profiles.active}/{@code spring.profiles.include}
         * properties that are already set.
         */
        private void initializeProfiles() {
            // The default profile for these purposes is represented as null. We add it
            // first so that it is processed first and has lowest priority.
            this.profiles.add(null);
            // 这里获取到主空
            Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
            // 获取其他配置文件
            this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
            // Any pre-existing active profiles set via property sources (e.g.
            // System properties) take precedence over those added in config files.
            addActiveProfiles(activatedViaProperty);
            if (this.profiles.size() == 1) { // only has null profile
                for (String defaultProfileName : this.environment.getDefaultProfiles()) {
                    Profile defaultProfile = new Profile(defaultProfileName, true);
                    this.profiles.add(defaultProfile);
                }
            }
        }
        
        // 搜索配置文件,注意配置文件是搜索得来的
        private Set<String> getSearchLocations() {
            // spring.config.location, 如果配置的指定目录,则从那里搜索
            if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
                return getSearchLocations(CONFIG_LOCATION_PROPERTY);
            }
            // 否则加载可能的目录 
            // - classpath:/
            // - classpath:/config/
            // - file:./
            // - file:./config/
            Set<String> locations = getSearchLocations(
                    CONFIG_ADDITIONAL_LOCATION_PROPERTY);
            locations.addAll(
                    asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
                            DEFAULT_SEARCH_LOCATIONS));
            return locations;
        }
复制代码

  接下来是 AnsiOutputApplicationListener 字符编码监听;

复制代码
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        Binder.get(environment)
                .bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
                .ifBound(AnsiOutput::setEnabled);
        AnsiOutput.setConsoleAvailable(environment
                .getProperty("spring.output.ansi.console-available", Boolean.class));
    }
复制代码

  日志监听 LoggingApplicationListener, :

复制代码
    @Override
    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();
        }
    }
    // 环境准备事件
    private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        if (this.loggingSystem == null) {
            this.loggingSystem = LoggingSystem
                    .get(event.getSpringApplication().getClassLoader());
        }
        initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }

    /**
     * Initialize the logging system according to preferences expressed through the
     * {@link Environment} and the classpath.
     * @param environment the environment
     * @param classLoader the classloader
     */
    protected void initialize(ConfigurableEnvironment environment,
            ClassLoader classLoader) {
        new LoggingSystemProperties(environment).apply();
        LogFile logFile = LogFile.get(environment);
        if (logFile != null) {
            logFile.applyToSystemProperties();
        }
        initializeEarlyLoggingLevel(environment);
        initializeSystem(environment, this.loggingSystem, logFile);
        // 第一次初始化日志系统
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }
复制代码

  接下来是 ClasspathLoggingApplicationListener 的环境准备监听,为空实现,只打印日志:

复制代码
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (logger.isDebugEnabled()) {
            if (event instanceof ApplicationEnvironmentPreparedEvent) {
                logger.debug("Application started with classpath: " + getClasspath());
            }
            else if (event instanceof ApplicationFailedEvent) {
                logger.debug(
                        "Application failed to start with classpath: " + getClasspath());
            }
        }
    }
复制代码

  接下来是 BackgroundPreinitializer, 后台管理监听,其不监听环境准备事件:

复制代码
    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent
                && preinitializationStarted.compareAndSet(false, true)) {
            performPreinitialization();
        }
        if ((event instanceof ApplicationReadyEvent
                || event instanceof ApplicationFailedEvent)
                && preinitializationStarted.get()) {
            try {
                preinitializationComplete.await();
            }
            catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
复制代码

  接下来是 DelegatingApplicationListener, 使其他自定义监听可以监听:

复制代码
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
                    ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
            if (delegates.isEmpty()) {
                return;
            }
            this.multicaster = new SimpleApplicationEventMulticaster();
            // 注册监听器
            for (ApplicationListener<ApplicationEvent> listener : delegates) {
                this.multicaster.addApplicationListener(listener);
            }
        }
        // 事件广播
        if (this.multicaster != null) {
            this.multicaster.multicastEvent(event);
        }
    }
复制代码

  接下来是 FileEncodingApplicationListener,

复制代码
    
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if (!environment.containsProperty("spring.mandatory-file-encoding")) {
            return;
        }
        String encoding = System.getProperty("file.encoding");
        String desired = environment.getProperty("spring.mandatory-file-encoding");
        if (encoding != null && !desired.equalsIgnoreCase(encoding)) {
            logger.error("System property 'file.encoding' is currently '" + encoding
                    + "'. It should be '" + desired
                    + "' (as defined in 'spring.mandatoryFileEncoding').");
            logger.error("Environment variable LANG is '" + System.getenv("LANG")
                    + "'. You could use a locale setting that matches encoding='"
                    + desired + "'.");
            logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL")
                    + "'. You could use a locale setting that matches encoding='"
                    + desired + "'.");
            throw new IllegalStateException(
                    "The Java Virtual Machine has not been configured to use the "
                            + "desired default character encoding (" + desired + ").");
        }
    }
    
    
    
    /**
     * Apply any {@link ApplicationContextInitializer}s to the context before it is
     * refreshed.
     * @param context the configured ApplicationContext (not refreshed yet)
     * @see ConfigurableApplicationContext#refresh()
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                    initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
复制代码

  初始化器列表:

 - org.springframework.boot.context.config.DelegatingApplicationContextInitializer
 - org.springframework.boot.context.ContextIdApplicationContextInitializer
 - org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
 - org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
 - org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
 - org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

  autoConfigurationReport 单例加载,加载资源文件,加载开始!

复制代码
    /**
     * Load beans into the application context.
     * @param context the context to load beans into
     * @param sources the sources to load
     */
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug(
                    "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        BeanDefinitionLoader loader = createBeanDefinitionLoader(
                getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        loader.load();
    }
    
    // contextLoaded() 事件监听
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextLoaded(context);
        }
    }
    
    
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        // 加载 bootstrap.properties, bootstrap-dev.properties...
        // 在 ConfigFileApplicationListener 的 ApplicationPreparedEvent 事件中触发
        this.initialMulticaster.multicastEvent(
                new ApplicationPreparedEvent(this.application, this.args, context));
    }
    // 如:    
    private void onApplicationPreparedEvent(ApplicationEvent event) {
        this.logger.replayTo(ConfigFileApplicationListener.class);
        addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
    }
复制代码

   重点关注, 即是进入原本的 SpringApplicationContext 容器中进行 各种bean 初始化了:

 

复制代码
    // refreshContext(context); 重点关注, 即是进入原本的 SpringApplicationContext 容器中进行 各种bean 初始化了
    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
    
    // 熟悉的模式
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
复制代码

  而在上下文刷新时,contextRefresh() 会触发这些 listeners:

 - org.springframework.boot.context.config.DelegatingApplicationListener
 - org.springframework.cloud.context.restart.RestartListener
 - org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener$ConditionEvaluationReportListener
 - org.springframework.boot.ClearCachesApplicationListener
 - org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$SharedMetadataReaderFactoryBean

  同样,DelegatingApplicationListener 是给其他监听器一个执行的时机的机会!

  RestartListener 则是负责响应应用的启动停止事件:

复制代码
    // RestartListener
    @Override
    public void onApplicationEvent(ApplicationEvent input) {
        if (input instanceof ApplicationPreparedEvent) {
            this.event = (ApplicationPreparedEvent) input;
            if (this.context == null) {
                this.context = this.event.getApplicationContext();
            }
        }
        else if (input instanceof ContextRefreshedEvent) {
            if (this.context != null && input.getSource().equals(this.context)
                    && this.event != null) {
                this.context.publishEvent(this.event);
            }
        }
        else {
            if (this.context != null && input.getSource().equals(this.context)) {
                this.context = null;
                this.event = null;
            }
        }
复制代码

  接下来是 ConditionEvaluationReportLoggingListener, 他会打印报告;

复制代码
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            ConditionEvaluationReportLoggingListener.this.onApplicationEvent(event);
        }
        
    protected void onApplicationEvent(ApplicationEvent event) {
        ConfigurableApplicationContext initializerApplicationContext = this.applicationContext;
        if (event instanceof ContextRefreshedEvent) {
            if (((ApplicationContextEvent) event)
                    .getApplicationContext() == initializerApplicationContext) {
                logAutoConfigurationReport();
            }
        }
        else if (event instanceof ApplicationFailedEvent
                && ((ApplicationFailedEvent) event)
                        .getApplicationContext() == initializerApplicationContext) {
            logAutoConfigurationReport(true);
        }
    }
复制代码

   让我们回到 bootstrap 加载开始的地方,BootstrapApplication 加载完成后,合并属性;

复制代码
    private ConfigurableApplicationContext bootstrapServiceContext(
            ConfigurableEnvironment environment, final SpringApplication application,
            String configName) {
        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
        MutablePropertySources bootstrapProperties = bootstrapEnvironment
                .getPropertySources();
        for (PropertySource<?> source : bootstrapProperties) {
            bootstrapProperties.remove(source.getName());
        }
        String configLocation = environment
                .resolvePlaceholders("${spring.cloud.bootstrap.location:}");
        Map<String, Object> bootstrapMap = new HashMap<>();
        bootstrapMap.put("spring.config.name", configName);
        // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail
        // force the environment to use none, because if though it is set below in the builder
        // the environment overrides it
        bootstrapMap.put("spring.main.web-application-type", "none");
        if (StringUtils.hasText(configLocation)) {
            bootstrapMap.put("spring.config.location", configLocation);
        }
        bootstrapProperties.addFirst(
                new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
        for (PropertySource<?> source : environment.getPropertySources()) {
            if (source instanceof StubPropertySource) {
                continue;
            }
            bootstrapProperties.addLast(source);
        }
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
        List<String> names = new ArrayList<>(SpringFactoriesLoader
                .loadFactoryNames(BootstrapConfiguration.class, classLoader));
        for (String name : StringUtils.commaDelimitedListToStringArray(
                environment.getProperty("spring.cloud.bootstrap.sources", ""))) {
            names.add(name);
        }
        // TODO: is it possible or sensible to share a ResourceLoader?
        SpringApplicationBuilder builder = new SpringApplicationBuilder()
                .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
                .environment(bootstrapEnvironment)
                // Don't use the default properties in this builder
                .registerShutdownHook(false).logStartupInfo(false)
                .web(WebApplicationType.NONE);
        if (environment.getPropertySources().contains("refreshArgs")) {
            // If we are doing a context refresh, really we only want to refresh the
            // Environment, and there are some toxic listeners (like the
            // LoggingApplicationListener) that affect global static state, so we need a
            // way to switch those off.
            builder.application()
                    .setListeners(filterListeners(builder.application().getListeners()));
        }
        List<Class<?>> sources = new ArrayList<>();
        for (String name : names) {
            Class<?> cls = ClassUtils.resolveClassName(name, null);
            try {
                cls.getDeclaredAnnotations();
            }
            catch (Exception e) {
                continue;
            }
            sources.add(cls);
        }
        AnnotationAwareOrderComparator.sort(sources);
        builder.sources(sources.toArray(new Class[sources.size()]));
        final ConfigurableApplicationContext context = builder.run();
        // gh-214 using spring.application.name=bootstrap to set the context id via
        // `ContextIdApplicationContextInitializer` prevents apps from getting the actual
        // spring.application.name
        // during the bootstrap phase.
        context.setId("bootstrap");
        // Make the bootstrap context a parent of the app context
        addAncestorInitializer(application, context);
        // It only has properties in it now that we don't want in the parent so remove
        // it (and it will be added back later)
        bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
        mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
        return context;
    }
复制代码

  其中涉及到的属性主要有:

 - org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource
 - org.springframework.core.env.MapPropertySource
 - org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource
 - org.springframework.boot.env.RandomValuePropertySource
 - org.springframework.core.env.MapPropertySource
 - org.springframework.boot.env.OriginTrackedMapPropertySource
 - org.springframework.boot.env.OriginTrackedMapPropertySource
 - org.springframework.core.env.MapPropertySource

  最后,再来看 BootstrapApplication 的 onApplicationEvent() 事件:

复制代码
    // BootstrapApplicationListener.onApplicationEvent()
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
                true)) {
            return;
        }
        // don't listen to events in a bootstrap context
        if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
            return;
        }
        ConfigurableApplicationContext context = null;
        String configName = environment
                .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
        for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
                .getInitializers()) {
            if (initializer instanceof ParentContextApplicationContextInitializer) {
                // 加载 BootstrapApplication
                context = findBootstrapContext(
                        (ParentContextApplicationContextInitializer) initializer,
                        configName);
            }
        }
        if (context == null) {
            context = bootstrapServiceContext(environment, event.getSpringApplication(),
                    configName);
        }
        apply(context, event.getSpringApplication(), environment);
    }
    
    // 注册beans...
    private void apply(ConfigurableApplicationContext context,
            SpringApplication application, ConfigurableEnvironment environment) {
        @SuppressWarnings("rawtypes")
        List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context,
                ApplicationContextInitializer.class);
        application.addInitializers(initializers
                .toArray(new ApplicationContextInitializer[initializers.size()]));
        addBootstrapDecryptInitializer(application);
    }

    private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context,
            Class<T> type) {
        List<T> result = new ArrayList<T>();
        // propertySourceBootstrapConfiguration, environmentDecryptApplicationListener
        for (String name : context.getBeanNamesForType(type)) {
            result.add(context.getBean(name, type));
        }
        AnnotationAwareOrderComparator.sort(result);
        return result;
    }
复制代码

  最后,回到最初加载的地方: BootstrapApplicationListener, 下一个:

复制代码
 - org.springframework.cloud.bootstrap.BootstrapApplicationListener
 - org.springframework.cloud.bootstrap.LoggingSystemShutdownListener
 - org.springframework.boot.context.config.ConfigFileApplicationListener
 - org.springframework.boot.context.config.AnsiOutputApplicationListener
 - org.springframework.boot.context.logging.LoggingApplicationListener
 - org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
 - org.springframework.boot.autoconfigure.BackgroundPreinitializer
 - org.springframework.boot.context.config.DelegatingApplicationListener
 - org.springframework.boot.context.FileEncodingApplicationListener
复制代码

  ClasspathLoggingApplicationListener, 加载 classpath 目录下的文件,即 jar 包系列:

Application started with classpath: [file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/charsets.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/deploy.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/access-bridge-64.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/cldrdata.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/dnsns.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/jaccess.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/jfxrt.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/localedata.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/nashorn.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunec.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunjce_provider.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunmscapi.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunpkcs11.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/zipfs.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/javaws.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jce.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jfr.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jfxswt.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jsse.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/management-agent.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/plugin.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/resources.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/rt.jar, file:/D:/www/java/richCash/target/classes/, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-amqp/2.0.5.RELEASE/spring-boot-starter-amqp-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter/2.0.5.RELEASE/spring-boot-starter-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot/2.0.5.RELEASE/spring-boot-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.0.5.RELEASE/spring-boot-autoconfigure-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.0.5.RELEASE/spring-boot-starter-logging-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.10.0/log4j-to-slf4j-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/logging/log4j/log4j-api/2.10.0/log4j-api-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar, file:/C:/Users/weiyong/.m2/repository/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/org/yaml/snakeyaml/1.19/snakeyaml-1.19.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-messaging/5.0.9.RELEASE/spring-messaging-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-beans/5.0.9.RELEASE/spring-beans-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/amqp/spring-rabbit/2.0.6.RELEASE/spring-rabbit-2.0.6.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/amqp/spring-amqp/2.0.6.RELEASE/spring-amqp-2.0.6.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/rabbitmq/amqp-client/5.4.1/amqp-client-5.4.1.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-tx/5.0.9.RELEASE/spring-tx-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-config/2.0.1.RELEASE/spring-cloud-starter-config-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter/2.0.1.RELEASE/spring-cloud-starter-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-context/2.0.1.RELEASE/spring-cloud-context-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/security/spring-security-rsa/1.0.5.RELEASE/spring-security-rsa-1.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/bouncycastle/bcpkix-jdk15on/1.56/bcpkix-jdk15on-1.56.jar, file:/C:/Users/weiyong/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.56/bcprov-jdk15on-1.56.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-config-client/2.0.1.RELEASE/spring-cloud-config-client-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.9.0/jackson-annotations-2.9.0.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.9.6/jackson-databind-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.9.6/jackson-core-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-aop/2.0.5.RELEASE/spring-boot-starter-aop-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-aop/5.0.9.RELEASE/spring-aop-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/retry/spring-retry/1.2.2.RELEASE/spring-retry-1.2.2.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-core/5.0.9.RELEASE/spring-core-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-jcl/5.0.9.RELEASE/spring-jcl-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-actuator/2.0.5.RELEASE/spring-boot-starter-actuator-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-actuator-autoconfigure/2.0.5.RELEASE/spring-boot-actuator-autoconfigure-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-actuator/2.0.5.RELEASE/spring-boot-actuator-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.6/jackson-datatype-jsr310-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/io/micrometer/micrometer-core/1.0.6/micrometer-core-1.0.6.jar, file:/C:/Users/weiyong/.m2/repository/org/hdrhistogram/HdrHistogram/2.1.10/HdrHistogram-2.1.10.jar, file:/C:/Users/weiyong/.m2/repository/org/latencyutils/LatencyUtils/2.0.3/LatencyUtils-2.0.3.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-bus-amqp/2.0.0.RELEASE/spring-cloud-starter-bus-amqp-2.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-stream-rabbit/2.0.1.RELEASE/spring-cloud-starter-stream-rabbit-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-stream-binder-rabbit/2.0.1.RELEASE/spring-cloud-stream-binder-rabbit-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-stream-binder-rabbit-core/2.0.1.RELEASE/spring-cloud-stream-binder-rabbit-core-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/rabbitmq/http-client/2.0.1.RELEASE/http-client-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-webflux/5.0.9.RELEASE/spring-webflux-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/projectreactor/ipc/reactor-netty/0.7.9.RELEASE/reactor-netty-0.7.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-codec-http/4.1.29.Final/netty-codec-http-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-handler-proxy/4.1.29.Final/netty-handler-proxy-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-codec-socks/4.1.29.Final/netty-codec-socks-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-transport-native-epoll/4.1.29.Final/netty-transport-native-epoll-4.1.29.Final-linux-x86_64.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-transport-native-unix-common/4.1.29.Final/netty-transport-native-unix-common-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-amqp/5.0.8.RELEASE/spring-integration-amqp-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-jmx/5.0.8.RELEASE/spring-integration-jmx-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-bus/2.0.0.RELEASE/spring-cloud-bus-2.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-stream/2.0.1.RELEASE/spring-cloud-stream-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-validation/2.0.5.RELEASE/spring-boot-starter-validation-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-tuple/1.0.0.RELEASE/spring-tuple-1.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/esotericsoftware/kryo-shaded/3.0.3/kryo-shaded-3.0.3.jar, file:/C:/Users/weiyong/.m2/repository/com/esotericsoftware/minlog/1.3.0/minlog-1.3.0.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-tuple/1.0.0.RELEASE/spring-integration-tuple-1.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-core/5.0.8.RELEASE/spring-integration-core-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-data-redis/2.0.5.RELEASE/spring-boot-starter-data-redis-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-redis/2.0.10.RELEASE/spring-data-redis-2.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-keyvalue/2.0.10.RELEASE/spring-data-keyvalue-2.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-commons/2.0.10.RELEASE/spring-data-commons-2.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-oxm/5.0.9.RELEASE/spring-oxm-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/lettuce/lettuce-core/5.0.5.RELEASE/lettuce-core-5.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/projectreactor/reactor-core/3.1.9.RELEASE/reactor-core-3.1.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/reactivestreams/reactive-streams/1.0.2/reactive-streams-1.0.2.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-common/4.1.29.Final/netty-common-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-transport/4.1.29.Final/netty-transport-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-buffer/4.1.29.Final/netty-buffer-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-resolver/4.1.29.Final/netty-resolver-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-handler/4.1.29.Final/netty-handler-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-codec/4.1.29.Final/netty-codec-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-data-rest/2.0.5.RELEASE/spring-boot-starter-data-rest-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.0.5.RELEASE/spring-boot-starter-json-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.6/jackson-datatype-jdk8-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.6/jackson-module-parameter-names-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-rest-webmvc/3.0.10.RELEASE/spring-data-rest-webmvc-3.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-rest-core/3.0.10.RELEASE/spring-data-rest-core-3.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/hateoas/spring-hateoas/0.25.0.RELEASE/spring-hateoas-0.25.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/plugin/spring-plugin-core/1.2.0.RELEASE/spring-plugin-core-1.2.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/atteo/evo-inflector/1.2.2/evo-inflector-1.2.2.jar, file:/C:/Users/weiyong/.m2/repository/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-jdbc/2.0.5.RELEASE/spring-boot-starter-jdbc-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/zaxxer/HikariCP/2.7.9/HikariCP-2.7.9.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-jdbc/5.0.9.RELEASE/spring-jdbc-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/xmlbeans/xmlbeans/2.6.0/xmlbeans-2.6.0.jar, file:/C:/Users/weiyong/.m2/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.0.5.RELEASE/spring-boot-starter-web-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.0.5.RELEASE/spring-boot-starter-tomcat-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/8.5.34/tomcat-embed-core-8.5.34.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/8.5.34/tomcat-embed-el-8.5.34.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/8.5.34/tomcat-embed-websocket-8.5.34.jar, file:/C:/Users/weiyong/.m2/repository/org/hibernate/validator/hibernate-validator/6.0.12.Final/hibernate-validator-6.0.12.Final.jar, file:/C:/Users/weiyong/.m2/repository/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar, file:/C:/Users/weiyong/.m2/repository/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/classmate/1.3.4/classmate-1.3.4.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-web/5.0.9.RELEASE/spring-web-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-webmvc/5.0.9.RELEASE/spring-webmvc-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-expression/5.0.9.RELEASE/spring-expression-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/spring/boot/mybatis-spring-boot-starter/1.3.2/mybatis-spring-boot-starter-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/1.3.2/mybatis-spring-boot-autoconfigure-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/mybatis/3.4.6/mybatis-3.4.6.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/mybatis-spring/1.3.2/mybatis-spring-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/mysql/mysql-connector-java/5.1.47/mysql-connector-java-5.1.47.jar, file:/C:/Users/weiyong/.m2/repository/org/objenesis/objenesis/2.6/objenesis-2.6.jar, file:/C:/Users/weiyong/.m2/repository/org/projectlombok/lombok/1.16.10/lombok-1.16.10.jar, file:/C:/Users/weiyong/.m2/repository/com/alibaba/fastjson/1.2.29/fastjson-1.2.29.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-openfeign/2.0.1.RELEASE/spring-cloud-starter-openfeign-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-openfeign-core/2.0.1.RELEASE/spring-cloud-openfeign-core-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-netflix-ribbon/2.0.1.RELEASE/spring-cloud-netflix-ribbon-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-netflix-archaius/2.0.1.RELEASE/spring-cloud-netflix-archaius-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-commons/2.0.1.RELEASE/spring-cloud-commons-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/security/spring-security-crypto/5.0.8.RELEASE/spring-security-crypto-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-core/9.5.1/feign-core-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-slf4j/9.5.1/feign-slf4j-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-hystrix/9.5.1/feign-hystrix-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/com/netflix/archaius/archaius-core/0.7.6/archaius-core-0.7.6.jar, file:/C:/Users/weiyong/.m2/repository/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar, file:/C:/Users/weiyong/.m2/repository/commons-configuration/commons-configuration/1.8/commons-configuration-1.8.jar, file:/C:/Users/weiyong/.m2/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar, file:/C:/Users/weiyong/.m2/repository/com/netflix/hystrix/hystrix-core/1.5.12/hystrix-core-1.5.12.jar, file:/C:/Users/weiyong/.m2/repository/io/reactivex/rxjava/1.3.8/rxjava-1.3.8.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-java8/9.5.1/feign-java8-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/org/codehaus/groovy/groovy-all/2.4.12/groovy-all-2.4.12.jar, file:/C:/Users/weiyong/.m2/repository/javax/persistence/persistence-api/1.0/persistence-api-1.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/httpcomponents/httpclient/4.5.6/httpclient-4.5.6.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/httpcomponents/httpcore/4.4.10/httpcore-4.4.10.jar, file:/C:/Users/weiyong/.m2/repository/commons-codec/commons-codec/1.11/commons-codec-1.11.jar, file:/C:/Users/weiyong/.m2/repository/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar, file:/C:/Users/weiyong/.m2/repository/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar, file:/C:/Users/weiyong/.m2/repository/redis/clients/jedis/2.9.0/jedis-2.9.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-pool2/2.5.0/commons-pool2-2.5.0.jar, file:/C:/Users/weiyong/.m2/repository/commons-beanutils/commons-beanutils/1.9.3/commons-beanutils-1.9.3.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-lang3/3.8/commons-lang3-3.8.jar, file:/C:/Users/weiyong/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar, file:/C:/Users/weiyong/.m2/repository/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar, file:/C:/Users/weiyong/.m2/repository/nuxeo/nuxeo-common-xmap/1.0/nuxeo-common-xmap-1.0.jar, file:/C:/Users/weiyong/.m2/repository/com/alibaba/druid/1.0.15/druid-1.0.15.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/lib/jconsole.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/lib/tools.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/poi/poi/3.15/poi-3.15.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-collections4/4.1/commons-collections4-4.1.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/poi/poi-ooxml/3.15/poi-ooxml-3.15.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/poi/poi-ooxml-schemas/3.15/poi-ooxml-schemas-3.15.jar, file:/C:/Users/weiyong/.m2/repository/com/github/virtuald/curvesapi/1.04/curvesapi-1.04.jar, file:/C:/Users/weiyong/.m2/repository/com/google/protobuf/protobuf-java/2.5.0/protobuf-java-2.5.0.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/openservices/aliyun-log-logback-appender/0.1.13/aliyun-log-logback-appender-0.1.13.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/openservices/aliyun-log/0.6.10/aliyun-log-0.6.10.jar, file:/C:/Users/weiyong/.m2/repository/net/sf/json-lib/json-lib/2.4/json-lib-2.4-jdk15.jar, file:/C:/Users/weiyong/.m2/repository/net/sf/ezmorph/ezmorph/1.0.6/ezmorph-1.0.6.jar, file:/C:/Users/weiyong/.m2/repository/commons-validator/commons-validator/1.4.0/commons-validator-1.4.0.jar, file:/C:/Users/weiyong/.m2/repository/commons-digester/commons-digester/1.8/commons-digester-1.8.jar, file:/C:/Users/weiyong/.m2/repository/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/openservices/log-loghub-producer/0.1.14/log-loghub-producer-0.1.14.jar, file:/C:/Users/weiyong/.m2/repository/com/google/code/gson/gson/2.8.5/gson-2.8.5.jar, file:/C:/Users/weiyong/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar, file:/C:/Users/weiyong/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar, file:/C:/Users/weiyong/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar, file:/C:/Users/weiyong/.m2/repository/joda-time/joda-time/2.9.9/joda-time-2.9.9.jar, file:/C:/Users/weiyong/.m2/repository/commons-fileupload/commons-fileupload/1.3.2/commons-fileupload-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/commons-io/commons-io/2.2/commons-io-2.2.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-mail/2.0.5.RELEASE/spring-boot-starter-mail-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-context/5.0.9.RELEASE/spring-context-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-context-support/5.0.9.RELEASE/spring-context-support-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar, file:/C:/Users/weiyong/.m2/repository/javax/activation/activation/1.1/activation-1.1.jar, file:/C:/Users/weiyong/.m2/repository/com/dangdang/elastic-job-lite-spring/2.1.5/elastic-job-lite-spring-2.1.5.jar, file:/C:/Users/weiyong/.m2/repository/com/dangdang/elastic-job-lite-core/2.1.5/elastic-job-lite-core-2.1.5.jar, file:/C:/Users/weiyong/.m2/repository/com/dangdang/elastic-job-common-core/2.1.5/elastic-job-common-core-2.1.5.jar, file:/C:/Users/weiyong/.m2/repository/org/quartz-scheduler/quartz/2.3.0/quartz-2.3.0.jar, file:/C:/Users/weiyong/.m2/repository/com/mchange/mchange-commons-java/0.2.11/mchange-commons-java-0.2.11.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-exec/1.3/commons-exec-1.3.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/oss/aliyun-sdk-oss/2.8.3/aliyun-sdk-oss-2.8.3.jar, file:/C:/Users/weiyong/.m2/repository/org/jdom/jdom/1.1/jdom-1.1.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/curator/curator-framework/2.10.0/curator-framework-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/curator/curator-client/2.10.0/curator-client-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/zookeeper/zookeeper/3.4.6/zookeeper-3.4.6.jar, file:/C:/Users/weiyong/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar, file:/C:/Users/weiyong/.m2/repository/jline/jline/0.9.94/jline-0.9.94.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty/3.7.0.Final/netty-3.7.0.Final.jar, file:/C:/Users/weiyong/.m2/repository/com/google/guava/guava/16.0.1/guava-16.0.1.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/curator/curator-recipes/2.10.0/curator-recipes-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-thymeleaf/2.0.5.RELEASE/spring-boot-starter-thymeleaf-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/thymeleaf/thymeleaf-spring5/3.0.9.RELEASE/thymeleaf-spring5-3.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/thymeleaf/thymeleaf/3.0.9.RELEASE/thymeleaf-3.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/attoparser/attoparser/2.0.4.RELEASE/attoparser-2.0.4.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/unbescape/unbescape/1.1.5.RELEASE/unbescape-1.1.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/thymeleaf/extras/thymeleaf-extras-java8time/3.0.1.RELEASE/thymeleaf-extras-java8time-3.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/alibaba/zhima/alipay-sdk/java20150312151947/alipay-sdk-java20150312151947.jar, file:/D:/Program%20Files/JetBrains/IntelliJ%20IDEA%202017.3.1/lib/idea_rt.jar, file:/C:/Users/weiyong/.IntelliJIdea2017.3/system/captureAgent/debugger-agent.jar, file:/D:/Program%20Files/JetBrains/IntelliJ%20IDEA%202017.3.1/lib/asm-all.jar]
View Code

   最后,再看一个 ServletWebServerApplicationContext 的refresh() , 其响应为:

复制代码
    // ServletWebServerApplicationContext.refresh()
    @Override
    public final void refresh() throws BeansException, IllegalStateException {
        try {
            super.refresh();
        }
        catch (RuntimeException ex) {
            stopAndReleaseWebServer();
            throw ex;
        }
    }
复制代码

  总体上来说,springboot 大量应用了事件监听机制,使各组件都基于事件运行,从而达到与框架解耦的作用;

posted @   阿牛20  阅读(3264)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示