SpringBoot的启动很简单,通用的代码如下:
@SpringBootApplicationpublic class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);}}
SpringApplication.run()方法实际执行的方法如下:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}
实则是:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}
初始化SpringApplication
SpringApplication的构造函数:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 调用WebApplicationType的静态方法deduceFromClasspath来选择启动的web应用类型this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}
(1)使用WebApplicationType的静态方法 deduceFromClasspath() 用来判断当前的应用是否是web应用,并设置到webApplicationType属性中去:
static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}//非web应用情况for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}
SERVLET_INDICATOR_CLASSES数组里面的是什么类:
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };
我们可以理解为: 如果获取不到这两个类:javax.servlet.Servlet”,”org.springframework.web.context.ConfigurableWebApplicationContext”, 则说明不是web应用。
(2)调用getSpringFactoriesInstances从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化,然后调用setInitializers方法设置到SpringApplication的initializers属性中。这个过程就是找出所有的应用程序初始化器。详情见获取初始化器。
# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
(3)调用getSpringFactoriesInstances从spring.factories文件中找出key为ApplicationListener的类并实例化,然后调用setListeners方法设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器。
获取初始化器
初始化器的获取由SpringApplication.getSpringFactoriesInstances方法完成:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicates// 读取ApplicationContextInitializer的实现类Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 实例化ApplicationContextInitializer的实现类List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}
SpringFactoriesLoader.loadFactoryNames方法获取ApplicationContextInitializer接口实现的类:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {// 获取接口类的名称String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}
private static Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {// 获取FACTORIES_RESOURCE_LOCATION(META-INF/spring.factories)的多个位置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()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}
