深入理解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. * *View Code@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 + "]"; } }
我們看一下在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