zoukankan      html  css  js  c++  java
  • SpringBoot学习(二)探究Springboot启动机制

    引言:

        SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次博主就跟你们一起探究一下SpringBoot的启动原理。

    目录

        启动流程图

        启动类

        启动分析

        启动总结

    启动流程图

    总览:

           上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。在下面的启动程序中我们会串联起结构中的主要功能。(摘自:SpringBoot启动流程解析)

    SpringBoot启动类

        从上面代码可以看出,别看它只是定义了 @SpringBootApplication 这个Annotation 和 调用SpringApplication.run方法,但是它们实现的功能可不是一星半点的。

    SpringApplication启动分析

        注:我这里是2.0.3版本的可能有些地方不太一样

        参考链接:深入理解SpringBoot之启动探究

    SpringaApplication初始化分析

    首先进入run方法

    会new一个SpringApplication实例,进入SpringApplication构造方法,它会调用initialize()进行初始化 

    首先它会去判断运行环境根据运行环境来创建是java得ApplicationContext对象或者是web得ApplicationContext对象。

    这里大家重点关注一下源代码中getSpringFacoriesInstances方法, ApplicationListener接口,ApplicationContextInitializer接口,这些接口都是通过SpringFactoriesLoader从META-INF/spring.factories文件里加载的

    看一下getSpringFacoriesInstances方法的源码

    这里有一个关键类叫做SpringFactoriesLoader 该类的主要作用就是去读取META-INF/spring.factories配置文件里配置的引导对象,我们来看一下代码:

    /*
     * Copyright 2002-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.core.io.support;
    
    import java.io.IOException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Enumeration;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.core.annotation.AnnotationAwareOrderComparator;
    import org.springframework.core.io.UrlResource;
    import org.springframework.lang.Nullable;
    import org.springframework.util.Assert;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.ConcurrentReferenceHashMap;
    import org.springframework.util.LinkedMultiValueMap;
    import org.springframework.util.MultiValueMap;
    import org.springframework.util.ReflectionUtils;
    import org.springframework.util.StringUtils;
    
    /**
     * General purpose factory loading mechanism for internal use within the framework.
     *
     * <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates
     * factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which
     * may be present in multiple JAR files in the classpath. The {@code spring.factories}
     * file must be in {@link Properties} format, where the key is the fully qualified
     * name of the interface or abstract class, and the value is a comma-separated list of
     * implementation class names. For example:
     *
     * <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
     *
     * where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}
     * and {@code MyServiceImpl2} are two implementations.
     *
     * @author Arjen Poutsma
     * @author Juergen Hoeller
     * @author Sam Brannen
     * @since 3.2
     */
    public abstract class SpringFactoriesLoader {
    
        /**
         * The location to look for factories.
         * <p>Can be present in multiple JAR files.
         */
        public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    
        private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    
        private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
    
    
        /**
         * Load and instantiate the factory implementations of the given type from
         * {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.
         * <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}.
         * <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}
         * to obtain all registered factory names.
         * @param factoryClass the interface or abstract class representing the factory
         * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
         * @see #loadFactoryNames
         * @throws IllegalArgumentException if any factory implementation class cannot
         * be loaded or if an error occurs while instantiating any factory
         */
        public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
            Assert.notNull(factoryClass, "'factoryClass' must not be null");
            ClassLoader classLoaderToUse = classLoader;
            if (classLoaderToUse == null) {
                classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
            }
            List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
            }
            List<T> result = new ArrayList<>(factoryNames.size());
            for (String factoryName : factoryNames) {
                result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
            }
            AnnotationAwareOrderComparator.sort(result);
            return result;
        }
    
        /**
         * Load the fully qualified class names of factory implementations of the
         * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
         * class loader.
         * @param factoryClass the interface or abstract class representing the factory
         * @param classLoader the ClassLoader to use for loading resources; can be
         * {@code null} to use the default
         * @see #loadFactories
         * @throws IllegalArgumentException if an error occurs while loading factory names
         */
        public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }
    
        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            MultiValueMap<String, String> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }
    
            try {
                Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                result = new LinkedMultiValueMap<>();
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                        List<String> factoryClassNames = Arrays.asList(
                                StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                        result.addAll((String) entry.getKey(), factoryClassNames);
                    }
                }
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }
    
        @SuppressWarnings("unchecked")
        private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
            try {
                Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
                if (!factoryClass.isAssignableFrom(instanceClass)) {
                    throw new IllegalArgumentException(
                            "Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
                }
                return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);
            }
        }
    
    }
    View Code

    通过SpringFactoriesLoader找到META-INF/spring.factories下ApplicationContextInitializer的实现类并将其实例化

    再看一下ApplicationListener的源码

    /*
     * Copyright 2002-2011 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context;
    
    /**
     * Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
     * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
     *
     * <p>Typically used within web applications that require some programmatic initialization
     * of the application context. For example, registering property sources or activating
     * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
     * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
     * for declaring a "contextInitializerClasses" context-param and init-param, respectively.
     *
     * <p>{@code ApplicationContextInitializer} processors are encouraged to detect
     * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
     * implemented or if the @{@link org.springframework.core.annotation.Order Order}
     * annotation is present and to sort instances accordingly if so prior to invocation.
     *
     * @author Chris Beams
     * @since 3.1
     * @see org.springframework.web.context.ContextLoader#customizeContext
     * @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
     * @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
     * @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
     */
    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    
        /**
         * Initialize the given application context.
         * @param applicationContext the application to configure
         */
        void initialize(C applicationContext);
    
    }
    View Code

    该接口在doc文档上描述很清楚了,在调用ConfigurableApplicationContext的refresh()之前进行的初始化操作

    SpringApplication run方法

    下面开始介绍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;
        }
    View Code

    1.启动计时

    stopWatch.start();

    2.创建了应用的监听器SpringApplicationRunListeners并开始监听

     该接口首先从META-INF/spring.factories文件里获取所有配置的SpringApplicationRunner ,那么这个接口时干啥的呢?

    我们来看一下源代码:

    /*
     * Copyright 2012-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.io.support.SpringFactoriesLoader;
    
    /**
     * Listener for the {@link SpringApplication} {@code run} method.
     * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
     * and should declare a public constructor that accepts a {@link SpringApplication}
     * instance and a {@code String[]} of arguments. A new
     * {@link SpringApplicationRunListener} instance will be created for each run.
     *
     * @author Phillip Webb
     * @author Dave Syer
     * @author Andy Wilkinson
     */
    public interface SpringApplicationRunListener {
    
        /**
         * Called immediately when the run method has first started. Can be used for very
         * early initialization.
         */
        void starting();
    
        /**
         * Called once the environment has been prepared, but before the
         * {@link ApplicationContext} has been created.
         * @param environment the environment
         */
        void environmentPrepared(ConfigurableEnvironment environment);
    
        /**
         * Called once the {@link ApplicationContext} has been created and prepared, but
         * before sources have been loaded.
         * @param context the application context
         */
        void contextPrepared(ConfigurableApplicationContext context);
    
        /**
         * Called once the application context has been loaded but before it has been
         * refreshed.
         * @param context the application context
         */
        void contextLoaded(ConfigurableApplicationContext context);
    
        /**
         * The context has been refreshed and the application has started but
         * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
         * ApplicationRunners} have not been called.
         * @param context the application context.
         * @since 2.0.0
         */
        void started(ConfigurableApplicationContext context);
    
        /**
         * Called immediately before the run method finishes, when the application context has
         * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
         * {@link ApplicationRunner ApplicationRunners} have been called.
         * @param context the application context.
         * @since 2.0.0
         */
        void running(ConfigurableApplicationContext context);
    
        /**
         * Called when a failure occurs when running the application.
         * @param context the application context or {@code null} if a failure occurred before
         * the context was created
         * @param exception the failure
         * @since 2.0.0
         */
        void failed(ConfigurableApplicationContext context, Throwable exception);
    
    }
    View Code

    看出其中包括:程序启动,环境准备,ApplicationContext准备加载,程序启动 运行 完成等。

    该接口默认有一个实现类EventPublishingRunListener至关重要大家需要了解一下:

    /*
     * Copyright 2012-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot.context.event;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.SpringApplicationRunListener;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.event.ApplicationEventMulticaster;
    import org.springframework.context.event.SimpleApplicationEventMulticaster;
    import org.springframework.context.support.AbstractApplicationContext;
    import org.springframework.core.Ordered;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.util.ErrorHandler;
    
    /**
     * {@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
     * @author Andy Wilkinson
     */
    public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    
        private final SpringApplication application;
    
        private final String[] args;
    
        private final SimpleApplicationEventMulticaster initialMulticaster;
    
        public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    
        @Override
        public void starting() {
            this.initialMulticaster.multicastEvent(
                    new ApplicationStartingEvent(this.application, this.args));
        }
    
        @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
            this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                    this.application, this.args, environment));
        }
    
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
    
        }
    
        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
            for (ApplicationListener<?> listener : this.application.getListeners()) {
                if (listener instanceof ApplicationContextAware) {
                    ((ApplicationContextAware) listener).setApplicationContext(context);
                }
                context.addApplicationListener(listener);
            }
            this.initialMulticaster.multicastEvent(
                    new ApplicationPreparedEvent(this.application, this.args, context));
        }
    
        @Override
        public void started(ConfigurableApplicationContext context) {
            context.publishEvent(
                    new ApplicationStartedEvent(this.application, this.args, context));
        }
    
        @Override
        public void running(ConfigurableApplicationContext context) {
            context.publishEvent(
                    new ApplicationReadyEvent(this.application, this.args, context));
        }
    
        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
            ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
                    this.args, context, exception);
            if (context != null && context.isActive()) {
                // Listeners have been registered to the application context so we should
                // use it at this point if we can
                context.publishEvent(event);
            }
            else {
                // An inactive context may not have a multicaster so we use our multicaster to
                // call all of the context's listeners instead
                if (context instanceof AbstractApplicationContext) {
                    for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                            .getApplicationListeners()) {
                        this.initialMulticaster.addApplicationListener(listener);
                    }
                }
                this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
                this.initialMulticaster.multicastEvent(event);
            }
        }
    
        private static class LoggingErrorHandler implements ErrorHandler {
    
            private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
    
            @Override
            public void handleError(Throwable throwable) {
                logger.warn("Error calling ApplicationEventListener", throwable);
            }
    
        }
    
    }
    View Code

    其中SimpleApplicationEventMulticaster这个类很重要

    看一下源码

    /*
     * Copyright 2002-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.event;
    
    import java.util.concurrent.Executor;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.core.ResolvableType;
    import org.springframework.lang.Nullable;
    import org.springframework.util.ErrorHandler;
    
    /**
     * Simple implementation of the {@link ApplicationEventMulticaster} interface.
     *
     * <p>Multicasts all events to all registered listeners, leaving it up to
     * the listeners to ignore events that they are not interested in.
     * Listeners will usually perform corresponding {@code instanceof}
     * checks on the passed-in event object.
     *
     * <p>By default, all listeners are invoked in the calling thread.
     * This allows the danger of a rogue listener blocking the entire application,
     * but adds minimal overhead. Specify an alternative task executor to have
     * listeners executed in different threads, for example from a thread pool.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     * @author Stephane Nicoll
     * @see #setTaskExecutor
     */
    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    
        @Nullable
        private Executor taskExecutor;
    
        @Nullable
        private ErrorHandler errorHandler;
    
    
        /**
         * Create a new SimpleApplicationEventMulticaster.
         */
        public SimpleApplicationEventMulticaster() {
        }
    
        /**
         * Create a new SimpleApplicationEventMulticaster for the given BeanFactory.
         */
        public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
            setBeanFactory(beanFactory);
        }
    
    
        /**
         * Set a custom executor (typically a {@link org.springframework.core.task.TaskExecutor})
         * to invoke each listener with.
         * <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},
         * executing all listeners synchronously in the calling thread.
         * <p>Consider specifying an asynchronous task executor here to not block the
         * caller until all listeners have been executed. However, note that asynchronous
         * execution will not participate in the caller's thread context (class loader,
         * transaction association) unless the TaskExecutor explicitly supports this.
         * @see org.springframework.core.task.SyncTaskExecutor
         * @see org.springframework.core.task.SimpleAsyncTaskExecutor
         */
        public void setTaskExecutor(@Nullable Executor taskExecutor) {
            this.taskExecutor = taskExecutor;
        }
    
        /**
         * Return the current task executor for this multicaster.
         */
        @Nullable
        protected Executor getTaskExecutor() {
            return this.taskExecutor;
        }
    
        /**
         * Set the {@link ErrorHandler} to invoke in case an exception is thrown
         * from a listener.
         * <p>Default is none, with a listener exception stopping the current
         * multicast and getting propagated to the publisher of the current event.
         * If a {@linkplain #setTaskExecutor task executor} is specified, each
         * individual listener exception will get propagated to the executor but
         * won't necessarily stop execution of other listeners.
         * <p>Consider setting an {@link ErrorHandler} implementation that catches
         * and logs exceptions (a la
         * {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER})
         * or an implementation that logs exceptions while nevertheless propagating them
         * (e.g. {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}).
         * @since 4.1
         */
        public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
            this.errorHandler = errorHandler;
        }
    
        /**
         * Return the current error handler for this multicaster.
         * @since 4.1
         */
        @Nullable
        protected ErrorHandler getErrorHandler() {
            return this.errorHandler;
        }
    
    
        @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);
                }
            }
        }
    
        private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
            return ResolvableType.forInstance(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 {
                doInvokeListener(listener, event);
            }
        }
    
        @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().getName())) {
                    // 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;
                }
            }
        }
    
        private boolean matchesClassCastMessage(String classCastMessage, String eventClassName) {
            // On Java 8, the message simply starts with the class name: "java.lang.String cannot be cast..."
            if (classCastMessage.startsWith(eventClassName)) {
                return true;
            }
            // On Java 9, the message contains the module name: "java.base/java.lang.String cannot be cast..."
            int moduleSeparatorIndex = classCastMessage.indexOf('/');
            if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClassName, moduleSeparatorIndex + 1)) {
                return true;
            }
            // Assuming an unrelated class cast failure...
            return false;
        }
    
    }
    View Code

    doInvokeListener方法,该方法用于执行事件的监听方法。该类最主要作用就是通知各个阶段的listener处理对应阶段的事件

    3.调用SpringApplicationRunListener的start方法

    看一下这个方法

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

    4.调用prepareEnvironment事件

        private ConfigurableEnvironment prepareEnvironment(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // Create and configure the environment
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            listeners.environmentPrepared(environment);
            bindToSpringApplication(environment);
            if (this.webApplicationType == WebApplicationType.NONE) {
                environment = new EnvironmentConverter(getClassLoader())
                        .convertToStandardEnvironmentIfNecessary(environment);
            }
            ConfigurationPropertySources.attach(environment);
            return environment;
        }
    View Code

    5.根据当前environment打印Banner

    也就是启动时控制台打印出的Spring标志 启动的Banner就是在这一步打印出来的  也可自定义Banner 链接:Spring Boot实例教程 - 自定义Banner

    6.创建的ApplicationContext对象

    这里会根据this.webEnvironment的属性值来确定创建的ApplicationContext对象

        /**
         * Strategy method used to create the {@link ApplicationContext}. By default this
         * method will respect any explicitly set application context or application context
         * class before falling back to a suitable default.
         * @return the application context (not yet refreshed)
         * @see #setApplicationContextClass(Class)
         */
        protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    switch (this.webApplicationType) {
                    case SERVLET:
                        contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                        break;
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                    }
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
    View Code

    如果是web环境那就创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext

    否则就创建org.springframework.context.annotation.AnnotationConfigApplicationContext

    7.调用prepareContext方法

        private void prepareContext(ConfigurableApplicationContext context,
                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments, Banner printedBanner) {
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            applyInitializers(context);
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }
    
            // Add boot specific singleton beans
            context.getBeanFactory().registerSingleton("springApplicationArguments",
                    applicationArguments);
            if (printedBanner != null) {
                context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
            }
    
            // Load the sources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[0]));
            listeners.contextLoaded(context);
        }
    View Code

    8.调用refreshContext

    该方法最终会执行AbstractApplicationContext的refresh()方法,我在这里贴一下源代码:

        @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();
                }
            }
        }
    View Code

    在这个方法里会初始化BeanFactory 初始化BeanFactoryPostProcessor 注册BeanPostProcessor 初始化MessageSource 注册事件监听器等操作。建议大家深入了解Spring的IOC加载原理

    9.调用afterRefresh方法

    	/**
    	 * Called after the context has been refreshed.
    	 * @param context the application context
    	 * @param args the application arguments
    	 */
    	protected void afterRefresh(ConfigurableApplicationContext context,
    			ApplicationArguments args) {
    	}

    10.启动计时结束

    stopWatch.stop();

    11.调用started方法

    	public void started(ConfigurableApplicationContext context) {
    		for (SpringApplicationRunListener listener : this.listeners) {
    			listener.started(context);
    		}
    	}
    

    12.调用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);
    		for (Object runner : new LinkedHashSet<>(runners)) {
    			if (runner instanceof ApplicationRunner) {
    				callRunner((ApplicationRunner) runner, args);
    			}
    			if (runner instanceof CommandLineRunner) {
    				callRunner((CommandLineRunner) runner, args);
    			}
    		}
    	}

    该方法会从IOC容器里找到ApplicationRunner或者CommandLineRunner并执行其run方法,当我们需要在SpringBoot程序启动时处理我们自己的逻辑,那么就可以实现上述接口

    13.启动异常处理

        private void handleRunFailure(ConfigurableApplicationContext context,
                Throwable exception,
                Collection<SpringBootExceptionReporter> exceptionReporters,
                SpringApplicationRunListeners listeners) {
            try {
                try {
                    handleExitCode(context, exception);
                    if (listeners != null) {
                        listeners.failed(context, exception);
                    }
                }
                finally {
                    reportFailure(exceptionReporters, exception);
                    if (context != null) {
                        context.close();
                    }
                }
            }
            catch (Exception ex) {
                logger.warn("Unable to close ApplicationContext", ex);
            }
            ReflectionUtils.rethrowRuntimeException(exception);
        }
    View Code

    至此run方法就执行完成了

    SpringBoot启动总结

    1.  SpringBoot启动时SpringApplicationRunListener接口的相关方法至关重要,它定义了启动时的各个“时间点”。

    2. SpringBoot可以从spring.factoies文件里读取配置的ApplicationListener

    3. META-INF文件夹下的spring.factoies文件是SpringBoot启动的核心文件,SpringFatoriesLoader会读取该文件夹下的相关配置作为引导

    4. SpringBoot启动时利用了事件机制,来发送启动时各个周期阶段的事件 

  • 相关阅读:
    累积进度图及本周PSP饼状图
    四则运算
    结对编程
    关于索引的误解
    Examining Pages
    索引存储原理
    【转】学习Robot Framework必须掌握的库—-BuiltIn库
    【转】Robot Framework用法总结
    【转】浅谈自动化测试框架设计
    【转】python虚拟环境--virtualenv
  • 原文地址:https://www.cnblogs.com/yanfeiLiu/p/9303999.html
Copyright © 2011-2022 走看看