標籤:對象 names end add 包含 src res hashset output
package com.microservice.framework;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MySpringAplication { public void run(String[] args) { SpringApplication sa = new SpringApplication(MySpringAplication.class); sa.run(args); }}
SpringBoot啟動過程:
1、構建SpringApplication對象
2、執行run()
一、構建SpringApplication對象
/** * The application context will load beans from the specified sources */ public SpringApplication(Object... sources) { initialize(sources); }
說明:
- 執行個體化該類的時候會載入bean到applicationContext中去
- 這裡的入參是MySpringApplication.class這樣一個Class<com.microservice.framework.MySpringApplication>對象
private final Set<Object> sources = new LinkedHashSet<Object>(); private boolean webEnvironment; private Class<?> mainApplicationClass; 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(); }
步驟:
- 將傳入的MySpringApplication.class對象放入Set集合
- 判斷是否是web環境
- 建立ApplicationInitializer列表
- 初始化ApplicationListener列表
- 初始化主類mainApplicationClass
1.1、將傳入的MySpringApplication.class對象放入Set集合
1.2、判斷是否是web環境:
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
說明:通過在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES這個數組中所包含的所有類(實際上就是2個類),如果存在那麼當前程式即是一個Web應用程式,反之則不然。
1.3、建立ApplicationContextInitializer列表
private List<ApplicationContextInitializer<?>> initializers; public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } 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 = new ArrayList<T>(names.size()); // Create instances from the names for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getConstructor(parameterTypes); T instance = (T) constructor.newInstance(args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } AnnotationAwareOrderComparator.sort(instances); return instances; }
步驟:
- 調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)來擷取所有Spring Factories的名字,(這裡是擷取了四個ApplicationContextInitializer實作類別的全類名,見下邊)
- 為每一個Spring Factories根據讀取到的名字建立其對象。(這裡建立了4個對象)
- 將建立好的對象列表排序並返回。
其中,SpringFactoriesLoader.loadFactoryNames(type, classLoader)如下:
/** * 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 the fully qualified class names of factory implementations of the * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given * class loader. */ 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); } }
META-INF/spring-factories
1 # Application Context Initializers2 org.springframework.context.ApplicationContextInitializer=3 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,4 org.springframework.boot.context.ContextIdApplicationContextInitializer,5 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,6 org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
說明:
以上四個類的作用:
至此,設定ApplicationContextInitialize就完成了。
總結:整個setInitializers實際上就是初始化了SpringApplication的屬性List<ApplicationContextInitializer<?>> initializers為一個ArrayList列表,該列表中有四個執行個體:
- ConfigurationWarningsApplicationContextInitializer的執行個體
- ContextIdApplicationContextInitializer的執行個體
- DelegatingApplicationContextInitializer執行個體
- ServerPortInfoApplicationContextInitializer執行個體
1.4、初始化ApplicationListener列表
private List<ApplicationListener<?>> listeners; /** * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication * and registered with the {@link ApplicationContext}. * @param listeners the listeners to set */ public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<ApplicationListener<?>>(); this.listeners.addAll(listeners); }
META-INF/spring-factories
# Application Listenersorg.springframework.context.ApplicationListener=org.springframework.boot.builder.ParentContextCloserApplicationListener,org.springframework.boot.context.FileEncodingApplicationListener,org.springframework.boot.context.config.AnsiOutputApplicationListener,org.springframework.boot.context.config.ConfigFileApplicationListener,org.springframework.boot.context.config.DelegatingApplicationListener,org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,org.springframework.boot.logging.ClasspathLoggingApplicationListener,org.springframework.boot.logging.LoggingApplicationListener
以上八個listener的作用如下:
至此,整個setListeners方法結束,初始化了一個包含以上8個ApplicationListener執行個體的List集合。
1.5、初始化主類mainApplicationClass
private Class<?> mainApplicationClass; 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()方法所在的主類Class對象,並賦值給SpringApplication的mainApplicationClass屬性。
至此,SpringApplication對象初始化完成了。
總結:整個SpringApplication初始化的過程,就是初始化了
- 一個包含入參MySpringApplication.class的sources的Set<Object>
- 一個當前環境是否是web環境的boolean webEnvironment
- 一個包含4個ApplicationContextInitializer執行個體的List
- 一個包含8個ApplicationListener執行個體的List
- 一個main方法所在的主類的Class對象。
注意:
本文基本參照http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow 完成,該文的作者已經解析的很好了,我這裡再抄一遍,只是為了加深記憶!!!
>>>>> 附3 springboot源碼解析 - 構建SpringApplication