• 深入理解SpringBoot之启动探究


      SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用。可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接下来我们就带着这两个问题来揭开SpringBoot启动过程的神秘面纱。

    一、基于Springframework的事件机制

      事件是SpringBoot的启动核心之一。对于事件我想大家都不陌生,在javaAWT中事件是在常见不过的了。

    1.1、JDK中的事件接口与类

      首先我们看一下EventObject,这个类定义了一个事件,该类中的source属性可以用来表示事件源(哪个对象触发的事件)

    /*
     * Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     */
    
    package java.util;
    
    /**
     * <p>
     * The root class from which all event state objects shall be derived.
     * <p>
     * All Events are constructed with a reference to the object, the "source",
     * that is logically deemed to be the object upon which the Event in question
     * initially occurred upon.
     *
     * @since JDK1.1
     */
    
    public class EventObject implements java.io.Serializable {
    
        private static final long serialVersionUID = 5516075349620653480L;
    
        /**
         * The object on which the Event initially occurred.
         */
        protected transient Object  source;
    
        /**
         * Constructs a prototypical Event.
         *
         * @param    source    The object on which the Event initially occurred.
         * @exception  IllegalArgumentException  if source is null.
         */
        public EventObject(Object source) {
            if (source == null)
                throw new IllegalArgumentException("null source");
    
            this.source = source;
        }
    
        /**
         * The object on which the Event initially occurred.
         *
         * @return   The object on which the Event initially occurred.
         */
        public Object getSource() {
            return source;
        }
    
        /**
         * Returns a String representation of this EventObject.
         *
         * @return  A a String representation of this EventObject.
         */
        public String toString() {
            return getClass().getName() + "[source=" + source + "]";
        }
    }
    View Code

      我们看一下在AWT中很经典的MouseEvent的类关系图:

      其次我们需要了解一下关于事件监听的接口EventListener:

    package java.util;
    
    /**
     * A tagging interface that all event listener interfaces must extend.
     * @since JDK1.1
     */
    public interface EventListener {
    }

      这个接口很简单,没有任何方法,但是JDK文档已经明确告诉我们:所有事件的监听必须继承此接口,那么我在贴出来一个MouseListener接口示例:

    /*
     * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     */
    
    package java.awt.event;
    
    import java.util.EventListener;
    
    /**
     * The listener interface for receiving "interesting" mouse events
     * (press, release, click, enter, and exit) on a component.
     * (To track mouse moves and mouse drags, use the
     * <code>MouseMotionListener</code>.)
     * <P>
     * The class that is interested in processing a mouse event
     * either implements this interface (and all the methods it
     * contains) or extends the abstract <code>MouseAdapter</code> class
     * (overriding only the methods of interest).
     * <P>
     * The listener object created from that class is then registered with a
     * component using the component's <code>addMouseListener</code>
     * method. A mouse event is generated when the mouse is pressed, released
     * clicked (pressed and released). A mouse event is also generated when
     * the mouse cursor enters or leaves a component. When a mouse event
     * occurs, the relevant method in the listener object is invoked, and
     * the <code>MouseEvent</code> is passed to it.
     *
     * @author Carl Quinn
     *
     * @see MouseAdapter
     * @see MouseEvent
     * @see <a href="https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html">Tutorial: Writing a Mouse Listener</a>
     *
     * @since 1.1
     */
    public interface MouseListener extends EventListener {
    
        /**
         * Invoked when the mouse button has been clicked (pressed
         * and released) on a component.
         */
        public void mouseClicked(MouseEvent e);
    
        /**
         * Invoked when a mouse button has been pressed on a component.
         */
        public void mousePressed(MouseEvent e);
    
        /**
         * Invoked when a mouse button has been released on a component.
         */
        public void mouseReleased(MouseEvent e);
    
        /**
         * Invoked when the mouse enters a component.
         */
        public void mouseEntered(MouseEvent e);
    
        /**
         * Invoked when the mouse exits a component.
         */
        public void mouseExited(MouseEvent e);
    }
    View Code

      我们可以看到MouseListener继承了EventListener接口,接口中的方法参数都为MouseEvent。

    1.2、spring中的事件类

      Spring中也给我们提供了一套事件处理机制,其中几个较为关键的接口和类分别是:

        ApplicationEvent

        ApplicationListener

        ApplicationEventPublisher

        ApplicationEventMulticaster

      下面我们来依次看一下这几个类与接口:

      ApplicationEvent:

    /*
     * Copyright 2002-2015 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;
    
    import java.util.EventObject;
    
    /**
     * Class to be extended by all application events. Abstract as it
     * doesn't make sense for generic events to be published directly.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     */
    public abstract class ApplicationEvent extends EventObject {
    
        /** use serialVersionUID from Spring 1.2 for interoperability */
        private static final long serialVersionUID = 7099057708183571937L;
    
        /** System time when the event happened */
        private final long timestamp;
    
    
        /**
         * Create a new ApplicationEvent.
         * @param source the object on which the event initially occurred (never {@code null})
         */
        public ApplicationEvent(Object source) {
            super(source);
            this.timestamp = System.currentTimeMillis();
        }
    
    
        /**
         * Return the system time in milliseconds when the event happened.
         */
        public final long getTimestamp() {
            return this.timestamp;
        }
    
    }
    View Code

      在这里我们可以明确看到该类直接继承EventObject

      ApplicationListener:

    /*
     * Copyright 2002-2015 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;
    
    import java.util.EventListener;
    
    /**
     * Interface to be implemented by application event listeners.
     * Based on the standard {@code java.util.EventListener} interface
     * for the Observer design pattern.
     *
     * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
     * that it is interested in. When registered with a Spring ApplicationContext, events
     * will be filtered accordingly, with the listener getting invoked for matching event
     * objects only.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     * @param <E> the specific ApplicationEvent subclass to listen to
     * @see org.springframework.context.event.ApplicationEventMulticaster
     */
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         * Handle an application event.
         * @param event the event to respond to
         */
        void onApplicationEvent(E event);
    
    }
    View Code

      我们可以看到该接口继承EventListener

      ApplicationEventPublisher:

    /*
     * Copyright 2002-2015 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;
    
    /**
     * Interface that encapsulates event publication functionality.
     * Serves as super-interface for {@link ApplicationContext}.
     *
     * @author Juergen Hoeller
     * @author Stephane Nicoll
     * @since 1.1.1
     * @see ApplicationContext
     * @see ApplicationEventPublisherAware
     * @see org.springframework.context.ApplicationEvent
     * @see org.springframework.context.event.EventPublicationInterceptor
     */
    public interface ApplicationEventPublisher {
    
        /**
         * Notify all <strong>matching</strong> listeners registered with this
         * application of an application event. Events may be framework events
         * (such as RequestHandledEvent) or application-specific events.
         * @param event the event to publish
         * @see org.springframework.web.context.support.RequestHandledEvent
         */
        void publishEvent(ApplicationEvent event);
    
        /**
         * Notify all <strong>matching</strong> listeners registered with this
         * application of an event.
         * <p>If the specified {@code event} is not an {@link ApplicationEvent},
         * it is wrapped in a {@link PayloadApplicationEvent}.
         * @param event the event to publish
         * @since 4.2
         * @see PayloadApplicationEvent
         */
        void publishEvent(Object event);
    
    }
    View Code

      这个接口比较重要,它使用来触发一个事件的(虽然方法的名称为发布事件),调用方法publishEvent过后,事件对应的listener将会执行相应的内容

      ApplicationEventMulticaster

      该接口管理ApplicationListener的同时可以执行listener监听事件的方法:

    /*
     * Copyright 2002-2017 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 org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.core.ResolvableType;
    
    /**
     * Interface to be implemented by objects that can manage a number of
     * {@link ApplicationListener} objects, and publish events to them.
     *
     * <p>An {@link org.springframework.context.ApplicationEventPublisher}, typically
     * a Spring {@link org.springframework.context.ApplicationContext}, can use an
     * ApplicationEventMulticaster as a delegate for actually publishing events.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     * @author Stephane Nicoll
     */
    public interface ApplicationEventMulticaster {
    
        /**
         * Add a listener to be notified of all events.
         * @param listener the listener to add
         */
        void addApplicationListener(ApplicationListener<?> listener);
    
        /**
         * Add a listener bean to be notified of all events.
         * @param listenerBeanName the name of the listener bean to add
         */
        void addApplicationListenerBean(String listenerBeanName);
    
        /**
         * Remove a listener from the notification list.
         * @param listener the listener to remove
         */
        void removeApplicationListener(ApplicationListener<?> listener);
    
        /**
         * Remove a listener bean from the notification list.
         * @param listenerBeanName the name of the listener bean to add
         */
        void removeApplicationListenerBean(String listenerBeanName);
    
        /**
         * Remove all listeners registered with this multicaster.
         * <p>After a remove call, the multicaster will perform no action
         * on event notification until new listeners are being registered.
         */
        void removeAllListeners();
    
        /**
         * Multicast the given application event to appropriate listeners.
         * <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
         * if possible as it provides a better support for generics-based events.
         * @param event the event to multicast
         */
        void multicastEvent(ApplicationEvent event);
    
        /**
         * Multicast the given application event to appropriate listeners.
         * <p>If the {@code eventType} is {@code null}, a default type is built
         * based on the {@code event} instance.
         * @param event the event to multicast
         * @param eventType the type of event (can be null)
         * @since 4.2
         */
        void multicastEvent(ApplicationEvent event, ResolvableType eventType);
    
    }
    View Code

      我们可以看一下其子类SimpleApplicationEventMulticaster 的源码:

    /*
     * Copyright 2002-2017 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.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 {
    
        private Executor taskExecutor;
    
        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(Executor taskExecutor) {
            this.taskExecutor = taskExecutor;
        }
    
        /**
         * Return the current task executor for this multicaster.
         */
        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(ErrorHandler errorHandler) {
            this.errorHandler = errorHandler;
        }
    
        /**
         * Return the current error handler for this multicaster.
         * @since 4.1
         */
        protected ErrorHandler getErrorHandler() {
            return this.errorHandler;
        }
    
    
        @Override
        public void multicastEvent(ApplicationEvent event) {
            multicastEvent(event, resolveDefaultEventType(event));
        }
    
        @Override
        public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            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 || msg.startsWith(event.getClass().getName())) {
                    // Possibly a lambda-defined listener which we could not resolve the generic event type for
                    Log logger = LogFactory.getLog(getClass());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Non-matching event type for listener: " + listener, ex);
                    }
                }
                else {
                    throw ex;
                }
            }
        }
    
    }
    View Code

      请大家看一下 doInvokeListener方法,该方法用于执行事件的监听方法

    1.3、基于Spring的自定义事件

      在这里我们模拟一个场景,当感到饥饿时,通知厨师做饭

      定义事件:

    package org.hzgj.spring.study.event;
    
    import org.springframework.context.ApplicationEvent;
    
    /**
     * 定义一个描饥饿状态的事件
     *
     * @author chen.nie
     * @date 2018/4/26
     **/
    public class HungryEvent extends ApplicationEvent {
        /**
         * Create a new ApplicationEvent.
         *
         * @param source the object on which the event initially occurred (never {@code null})
         */
        public HungryEvent(Object source) {
            super(source);
        }
    }
    View Code

      定义Person:

    package org.hzgj.spring.study.event;
    
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.stereotype.Component;
    
    /**
     * Person类,如果属性hungry的值为0,则通知厨师做饭吃。
     */
    @Component
    public class Person implements ApplicationEventPublisherAware {
    
        private int hungry;
    
        private String name;
    
        public int getHungry() {
            return hungry;
        }
    
        public void setHungry(int hungry) {
            this.hungry = hungry;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    
        private ApplicationEventPublisher applicationEventPublisher;
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
    
        public void isNeedEat() {
            if (this.hungry == 0) {
                System.out.println("太饿了,需要吃东西");
                new Thread(() -> this.applicationEventPublisher.publishEvent(new HungryEvent(this))).start();
                System.out.println("通知完毕");
            }
        }
    
    
    }
    View Code

      注意这里面利用spring的aware模式拿到ApplicationEventPublisher对象,在Spring里有若干个Aware,比如说ApplicationContextAware BeanFactoryAware等。

       定义厨师类:

    package org.hzgj.spring.study.event;
    
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    /**
     * 厨师类用于对饥饿事件的监听...
     *
     * @author chen.nie
     * @date 2018/4/26
     **/
    @Component
    public class Chef implements ApplicationListener<HungryEvent> {
        @Override
        public void onApplicationEvent(HungryEvent event) {
            if (event.getSource() instanceof Person) {
    
                Person person = (Person) event.getSource();
                System.out.println(person.getName() + "饿了,开始做饭");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("做饭完毕....开始吃吧");
            }
        }
    }
    View Code

      spring-config.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
    
        <context:component-scan base-package="org.hzgj"/>
    
    
    </beans>
    View Code

      Main方法:

    package org.hzgj.spring.study;
    
    import org.hzgj.spring.study.event.Person;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import javax.naming.NamingException;
    import java.io.IOException;
    
    public class Main {
        public static void main(String[] args) throws IOException, NamingException {
    
            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
            Person person = applicationContext.getBean(Person.class);
            person.setHungry(0);
            person.setName("admin");
            person.isNeedEat();
        }
    }
    View Code

      执行Main方法后,我们可以看到如下结果:

    二、SpringApplication启动分析

     2.1、SpringApplication初始化分析

     在这里我们先追踪一下SpringApplication.run的方法:

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

      该方法会创建SpringApplication对象,我们继续看一下关键代码:

     

    //......
        /**
         * Create a new {@link SpringApplication} instance. The application context will load
         * beans from the specified sources (see {@link SpringApplication class-level}
         * documentation for details. The instance can be customized before calling
         * {@link #run(String...)}.
         * @param sources the bean sources
         * @see #run(Object, String[])
         * @see #SpringApplication(ResourceLoader, Object...)
         */
        public SpringApplication(Object... sources) {
            initialize(sources);
        }
    
    
    //......
    
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private void initialize(Object[] sources) {
            if (sources != null && sources.length > 0) {
                this.sources.addAll(Arrays.asList(sources));
            }
            this.webEnvironment = deduceWebEnvironment();
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }

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

      其中getSpringFactoriesInstances的关键代码:

    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<String>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

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

    /*
     * Copyright 2002-2016 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.lang.reflect.Constructor;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Enumeration;
    import java.util.List;
    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.util.Assert;
    import org.springframework.util.ClassUtils;
    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 {
    
        private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    
        /**
         * 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";
    
    
        /**
         * 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 in accordance with the {@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, 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<T>(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, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            try {
                Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                List<String> result = new ArrayList<String>();
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                        "] 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() + "]");
                }
                Constructor<?> constructor = instanceClass.getDeclaredConstructor();
                ReflectionUtils.makeAccessible(constructor);
                return (T) constructor.newInstance();
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);
            }
        }
    
    }
    View Code

      这里面的ApplicationListener略微特殊,它被定义到META-INF/spring.factories里,该监听器主要监听SpringApplicationEvent事件,SpringApplicationEvent有如下子类:

       1.  ApplicationStartingEvent

       2. ApplicationEnvironmentPreparedEvent

       3. ApplicationPreparedEvent

       4. ApplicationFailedEvent

       5. ApplicationReadyEvent

      ApplicationContextInitializer该接口

    /*
     * 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()之前进行的初始化操作,比如说:激活profile , 注册PropertySource , FrameworkServlet(DispacherServlet的父类)设置 contextConfigLocation等。

    2.2、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;
            FailureAnalyzers analyzers = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                analyzers = new FailureAnalyzers(context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                listeners.finished(context, null);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                return context;
            }
            catch (Throwable ex) {
                handleRunFailure(context, listeners, analyzers, ex);
                throw new IllegalStateException(ex);
            }
        }

    1. 获取SpringApplicationRunListener

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

      该接口首先从META-INF/spring.factories文件里获取所有配置的SpringApplicationRunner ,那么这个接口时干啥的呢?我们来看一下源代码:

    /*
     * Copyright 2012-2016 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
     */
    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);
    
        /**
         * Called immediately before the run method finishes.
         * @param context the application context or null if a failure occurred before the
         * context was created
         * @param exception any run exception or null if run completed successfully.
         */
        void finished(ConfigurableApplicationContext context, Throwable exception);
    
    }
    View Code

      其实简单点来说就是在SpringBoot启动过程中各个阶段需要做的事情,阶段包括:程序准备启动,准备环境,ApplicationContext准备加载,程序启动完成等等。

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

    /*
     * Copyright 2012-2017 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
     */
    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
        @SuppressWarnings("deprecation")
        public void starting() {
            this.initialMulticaster
                    .multicastEvent(new ApplicationStartedEvent(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 finished(ConfigurableApplicationContext context, Throwable exception) {
            SpringApplicationEvent event = getFinishedEvent(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);
                    }
                }
                if (event instanceof ApplicationFailedEvent) {
                    this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
                }
                this.initialMulticaster.multicastEvent(event);
            }
        }
    
        private SpringApplicationEvent getFinishedEvent(
                ConfigurableApplicationContext context, Throwable exception) {
            if (exception != null) {
                return new ApplicationFailedEvent(this.application, this.args, context,
                        exception);
            }
            return new ApplicationReadyEvent(this.application, this.args, context);
        }
    
        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的属性,根据前面分析,该属性就是执行关于SpringApplicationEvent的事件监听方法的。该类最主要作用就是通知各个阶段的listener处理对应阶段的事件

     

    2、调用所有的SpringApplicationRunListenner的start方法

      我们可以看一下SpringApplicationRunListeners类里的方法:

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

      

    3、执行prepareEnvironment方法

    public void environmentPrepared(ConfigurableEnvironment environment) {
            for (SpringApplicationRunListener listener : this.listeners) {
                listener.environmentPrepared(environment);
            }
        }

    4、根据当前的Environment打印Banner

    5、创建ConfigurableApplicationContext对象与FailureAnalyzers

      在这里会根据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 {
                    contextClass = Class.forName(this.webEnvironment
                            ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
        }

      如果是web环境那就创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext ,否则就创建org.springframework.context.annotation.AnnotationConfigApplicationContext

    6、调用prepareContext方法

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

    7、调用 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加载原理

    8、执行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) {
            callRunners(context, args);
        }
    
        private void callRunners(ApplicationContext context, ApplicationArguments args) {
            List<Object> runners = new ArrayList<Object>();
            runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
            runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
            AnnotationAwareOrderComparator.sort(runners);
            for (Object runner : new LinkedHashSet<Object>(runners)) {
                if (runner instanceof ApplicationRunner) {
                    callRunner((ApplicationRunner) runner, args);
                }
                if (runner instanceof CommandLineRunner) {
                    callRunner((CommandLineRunner) runner, args);
                }
            }
        }

    private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
    (runner).run(args);
    }
    catch (Exception ex) {
    throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
    }
    }
     

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

    9、调用 listeners.finished方法

    public void finished(ConfigurableApplicationContext context, Throwable exception) {
            for (SpringApplicationRunListener listener : this.listeners) {
                callFinishedListener(listener, context, exception);
            }
        }
    
    private void callFinishedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) { try { listener.finished(context, exception); } catch (Throwable ex) { if (exception == null) { ReflectionUtils.rethrowRuntimeException(ex); } if (this.log.isDebugEnabled()) { this.log.error("Error handling failed", ex); } else { String message = ex.getMessage(); message = (message == null ? "no error message" : message); this.log.warn("Error handling failed (" + message + ")"); } } }

    10、启动时的异常处理

        private void handleRunFailure(ConfigurableApplicationContext context,
                SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,
                Throwable exception) {
            try {
                try {
                    handleExitCode(context, exception);
                    listeners.finished(context, exception);
                }
                finally {
                    reportFailure(analyzers, exception);
                    if (context != null) {
                        context.close();
                    }
                }
            }
            catch (Exception ex) {
                logger.warn("Unable to close ApplicationContext", ex);
            }
            ReflectionUtils.rethrowRuntimeException(exception);
        }

      我们可以看到在SpringApplicationRunnerListener的作用至关重要,几乎每做一件事情都涉及到此接口的方法 ,另外 EventPublishingRunListener会在各个阶段通知各个listener处理启动周期内各个阶段性事件

    三、测试示例

      通过在META-INF/spring.factories里配置引导类,来验证一下我们上述分析的启动过程

      1、创建MyBootStrapApplicationListener示例:

    package com.hzgj.lyrk.member.applicationlistener;
    
    import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
    import org.springframework.context.ApplicationListener;
    /**
     * META-INF/spring.factories 配置的listener测试
    * @author chen.nie
    * @date 2018/4/26
    **/
    public class MyBootStrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
        @Override
        public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
            System.out.println("BootStrapApplicationListener");
        }
    }
    View Code

      2、创建MyCommandRunner

    package com.hzgj.lyrk.member.commandlinerunner;
    
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    
    /**
     * commandLineRunner测试
     *
     * @author chen.nie
     * @date 2018/4/26
     **/
    @Component
    public class MyCommandRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            System.out.println("MyCommandRunner execute .....");
        }
    }
    View Code

      3、在spring.factories配置ApplicationListener

    org.springframework.context.ApplicationListener=
      com.hzgj.lyrk.member.applicationlistener.MyBootStrapApplicationListener

      当我们启动SpringBoot项目时,可以发现如下结果:

     

     我们可以发现我们配置的ApplicationListener在最开始就会执行,而CommandLineRunner在最后才执行

    四、SpringBoot启动总结

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

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

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

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

  • 相关阅读:
    ASP.NET Web API中通过URI显示实体中的部分字段
    ASP.NET Web API中把分页信息放Header中返回给前端
    写一个针对IQueryable<T>的扩展方法支持动态排序
    Perl处理和收走子进程(退出状态码和wait)
    Perl多进程
    Perl信号处理
    大话西游之大圣娶亲中的至尊宝和紫霞仙子
    Perl List::Util模块用法详解
    Python面向对象:杂七杂八的知识点
    Python迭代和解析(5):搞懂生成器和yield机制
  • 原文地址:https://www.cnblogs.com/niechen/p/8947973.html
Copyright © 2020-2023  润新知