/** * springboot應用的啓動入口 */ @RestController @SpringBootApplication public class SampleApplication { @RequestMapping("/") public String sayhello(){ return "hello world"; } public static void main(String[] args) { // TODO Auto-generated method stub SpringApplication.run(SampleApplication.class, args); } }
Spring Boot程序的啓動入口就一行代碼,SpringApplication.run(SampleApplication.class,args)java
SpringApplication的靜態run方法內部實際上是new了一個SpringApplication的實例,構造函數保存了SourceClass。在SpringApplication實例初始化的時候,它作了幾件事情:react
推斷應用入口類web
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = deduceWebApplicationType(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
下面具體從源碼來看看每一步是怎麼作的spring
private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
//相關常量
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";springboot
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };app
可能會出現三種結果:框架
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
這裏出現了一個新的概念 - 初始化器。函數
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } // 這裏的入參type就是ApplicationContextInitializer.class private <T> Collection<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<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
這裏面首先會根據入參type讀取全部的names(是一個String集合),而後根據這個集合來完成對應的實例化操做。spring-boot
// 這裏的入參type就是ApplicationContextInitializer.class
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) return result; try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { List<String> factoryClassNames = Arrays.asList( StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); result.addAll((String) entry.getKey(), factoryClassNames); } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
/**
* 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";ui
這個方法會嘗試從類路徑的META-INF/spring.factories處讀取相應配置文件,而後進行遍歷,讀取配置文件中Key爲:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure這個包爲例,它的META-INF/spring.factories部分定義以下所示:
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
所以這兩個類名會被讀取出來,而後放入到集合中,準備開始下面的實例化操做:
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass .getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }
類加載,確認被加載的類確實是org.springframework.context.ApplicationContextInitializer的子類,而後就是獲得構造器進行初始化,最後放入到實例列表中。
所以,所謂的初始化器就是org.springframework.context.ApplicationContextInitializer的實現類,這個接口是這樣定義的:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); }
根據類文檔,這個接口的主要功能是:
在Spring上下文被刷新以前進行初始化的操做。典型地好比在Web應用中,註冊Property Sources或者是激活Profiles。Property Sources比較好理解,就是配置文件。Profiles是Spring爲了在不一樣環境下(如DEV,TEST,PRODUCTION等),加載不一樣的配置項而抽象出來的一個實體。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 這裏的入參type是:org.springframework.context.ApplicationListener.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<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<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
能夠發現,這個加載相應的類名,而後完成實例化的過程和上面在設置初始化器時一模一樣,一樣,仍是以spring-boot-autoconfigure這個包中的spring.factories爲例,看看相應的Key-Value:
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
至於ApplicationListener接口,它是Spring框架中一個至關基礎的接口了,代碼以下:
@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); }
this.mainApplicationClass = deduceMainApplicationClass();
這個方法的實現有點意思:
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
它經過構造一個運行時異常,經過異常棧中方法名爲main的棧幀來獲得入口類的名字。
至此,對於SpringApplication實例的初始化過程就結束了。
SpringApplication實例的run方法內部都作了哪些呢:
1.建立SpringApplication本身的SpringApplicationRunListener。遍歷執行全部經過SpringFactoriesLoader能夠查找到並加載的SpringApplicationRunListener。調用.它們的started()方法,告訴這些SpringApplicationRunListener,「嘿,SpringBoot應用要開始執行咯!」。
2.建立並配置當前Spring Boot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。
3. 遍歷調用全部SpringApplicationRunListener的environmentPrepared()的方法,告訴他們:「當前SpringBoot應用使用的Environment準備好了咯!」。
4. 若是SpringApplication的showBanner屬性被設置爲true,則打印banner。
5.建立ApplicationContext,由於如今應用類型是Servlet,建立的是AnnotationConfigServletWebServerApplicationContext。
6.對ApplicationContext進行配置,將以前準備好的Environment設置給建立好的ApplicationContext使用。還有配置BeanNameGenerator、ResourceLoader等。
7.ApplicationContext建立好以後,而後遍歷ApplicationContextInitializer的initialize(applicationContext)方法來對已經建立好的ApplicationContext進行進一步的處理。這裏的ApplicationContextInitializer就是建立SpringApplication實例時設置的初始化器。
8. 遍歷調用全部SpringApplicationRunListener的contextPrepared()方法。
9. 最核心的一步,將以前經過@EnableAutoConfiguration獲取的全部配置以及其餘形式的IoC容器配置加載到已經準備完畢的ApplicationContext。
10.遍歷調用全部SpringApplicationRunListener的contextLoaded()方法。
11.啓動ApplicationContext,AnnotationConfigServletWebServerApplicationContext繼承了SpringFrameWork自己提供的GenericWebApplicationContext提供的功能並進行了擴展,以支持配置並啓動Embed Tomcat。
(1)、對BeanFactory進行一些初始化配置。
(2)、執行BeanFactoryPostProcessor,其中包括對BeanDefinition的進一步處理。最重要的是ConfigurationClassPostProcessor,用來解析處理全部@Configuration標籤類,並將Bean定義註冊到BeanFactory中。由於@SpringBootApplication中包含了@EnableAutoConfiguration的meta-annotation,會進行自動配置處理,基本原理是判斷工程依賴了哪些第三方組件並對其進行自動化配置,這樣處理完@Configuration標籤後,BeanFactory中就已經有大量的Bean定義了。
(3)、註冊BeanPostProcessor,這些Processor會在首次getBean時執行。主要功能包括進行Autowire、Required等標籤的處理,完成自動綁定等功能。也有特殊的關於WebServleterFactory的後續處理。
(4)、在ApplicationContext的onRefresh方法中會對Web容器(Tomcat)進行配置,包括註冊Servlet、Filter、Listener等。
(5)、在ApplicationContext的finishRefresh方法中啓動Web容器(Tomcat),完成應用的啓動。