>>>>> 附3 springboot源码解析 - 构建SpringApplication

黄沙百战穿金甲,不破楼兰终不还。这篇文章主要讲述> > > > > 附3 springboot源码解析 - 构建SpringApplication相关的知识,希望能为你提供帮助。

package com.microservice.framework; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public 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 Initializers 2 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

说明:
  • 从所有jar获取所有的META-INF/spring-factories文件。(这里只有spring-boot-1.3.0.RELEASE.jar下有一个)
  • 遍历每一个spring-factories文件,并获取其下key为factoryClass.getName()(这里是入参org.springframework.context.ApplicationContextInitializer)的value(这里有以上四个ApplicationContextInitializer实现类)
以上四个类的作用:
&gt;&gt;&gt;&gt;&gt; 附3 springboot源码解析 - 构建SpringApplication

文章图片

 
【> > > > > 附3 springboot源码解析 - 构建SpringApplication】至此,设置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 Listeners org.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的作用如下:
&gt;&gt;&gt;&gt;&gt; 附3 springboot源码解析 - 构建SpringApplication

文章图片

至此,整个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  完成,该文的作者已经解析的很好了,我这里再抄一遍,只是为了加深记忆!!!

    推荐阅读