博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解SpringBoot之启动探究
阅读量:6393 次
发布时间:2019-06-23

本文共 54068 字,大约阅读时间需要 180 分钟。

  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;/** * 

* The root class from which all event state objects shall be derived. *

* 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 * MouseMotionListener.) * 

* The class that is interested in processing a mouse event * either implements this interface (and all the methods it * contains) or extends the abstract MouseAdapter class * (overriding only the methods of interest). *

* The listener object created from that class is then registered with a * component using the component's addMouseListener * 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 MouseEvent is passed to it. * * @author Carl Quinn * * @see MouseAdapter * @see MouseEvent * @see Tutorial: Writing a Mouse Listener * * @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. * *

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

the specific ApplicationEvent subclass to listen to * @see org.springframework.context.event.ApplicationEventMulticaster */public interface ApplicationListener
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 matching 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 matching listeners registered with this * application of an event. *

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. * *

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. *

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. *

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. *

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. * *

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. * *

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. *

Default is equivalent to {

@link org.springframework.core.task.SyncTaskExecutor}, * executing all listeners synchronously in the calling thread. *

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. *

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. *

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,则通知厨师做饭吃。 */@Componentpublic 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 **/@Componentpublic class Chef implements ApplicationListener
{ @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:

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 
Collection
getSpringFactoriesInstances(Class
type, Class
[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set
names = new LinkedHashSet
( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List
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. * * 

{

@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: * *

example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
* * 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. *

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. *

The returned factories are sorted in accordance with the {

@link AnnotationAwareOrderComparator}. *

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
List
loadFactories(Class
factoryClass, ClassLoader classLoader) { Assert.notNull(factoryClass, "'factoryClass' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List
factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } List
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
loadFactoryNames(Class
factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration
urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List
result = new ArrayList
(); 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 instantiateFactory(String instanceClassName, Class
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}. * *

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. * *

{

@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
{ /** * 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. *

* 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 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);            }        }    }
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
{ @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 **/@Componentpublic 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启动时利用了事件机制,来发送启动时各个周期阶段的事件 

转载地址:http://iplha.baihongyu.com/

你可能感兴趣的文章
ACCESS模糊查询出现"内存溢出"原因是日文片假名
查看>>
Error setting expression 'XXX' with value 设置表达式“XXX”时出错 解决方法
查看>>
javascript获取url参数和script标签中获取url参数
查看>>
CF359D:Pair of Numbers(数论)
查看>>
进制转换展示
查看>>
张泉灵:做投资这半年哭过的时间比前十年都多
查看>>
c++将bool变量以文字形式打印
查看>>
洛谷P1111 修复公路 并查集 图论 最小生成树
查看>>
微名汇-微信公众平台功能开发(微信聊天机器人)
查看>>
A2W和W2A :很好的多字节和宽字节字符串的转换宏
查看>>
我个人的javascript和css命名规范
查看>>
kylin的安装与配置
查看>>
Android Intent的setClass和setClassName的区别
查看>>
php-fpm nginx 使用 curl 请求 https 出现 502 错误
查看>>
西宁海关首次对外展示截获500余件有害生物标本
查看>>
泸州移动能源产业园首片薄膜电池组件成功下线
查看>>
韩国瑜会见陆委会主委陈明通:别给高雄念紧箍咒
查看>>
交通部:加大人工售票力度保障农民工春运出行
查看>>
物联网的学术层、应用层和行为层的基本介绍
查看>>
初探github(一)
查看>>