springboot源码探究1--SpringApplication初始化确定web程序类型
本文以一个基础的springboot,mvc服务如何启动加载。进行探究,做一下源码探究记录
首先搭建一个能正常启动的springboot的mvc项目。
@EnableFeignClients @EnableEurekaClient @SpringBootApplication @RestController public class AuthServiceApplication { @RequestMapping("/health") public String home() { return "Hello World"; } public static void main(String[] args) { SpringApplication.run(AuthServiceApplication.class, args); } }
点击进入
SpringApplication.run(AuthServiceApplication.class, args);
run方法,继续点击进入 SpringApplication,构造方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
可以看到进入如下构造方法public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader;//类加载器,从上面点击进入发现此时类加载器是null,后面代码会指定默认类加载器
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();//获取当前启动项目的类型,枚举有三种类型:NONE,SERVLET(通常的Java服务器项目),REACTIVE(是一个完全的reactive并且非阻塞的web框架,后续再做探究)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//获取当前程序的上下文初始化类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//获取当前程序的所有监听器
this.mainApplicationClass = deduceMainApplicationClass();
}
咱们先着重看一下this.webApplicationType = WebApplicationType.deduceFromClasspath();进入代码会看到如下代码(代码片段)
1 static WebApplicationType deduceFromClasspath() { 2 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) 3 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { 4 return WebApplicationType.REACTIVE; 5 } 6 for (String className : SERVLET_INDICATOR_CLASSES) { 7 if (!ClassUtils.isPresent(className, null)) { 8 return WebApplicationType.NONE; 9 } 10 } 11 return WebApplicationType.SERVLET; 12 }
如上ClassUtils.isPresent方法,进入内部查看代码发现,会去加载指定的 WEBFLUX_INDICATOR_CLASS等类,如果查找不到就返回false.然后我们看下图,通过spring官方下载的快速启动的webclent项目,可以知道pom文件需要配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
查看源代码类图,可以找到如下图,而且查看项目引用jar可以得到上面代码片段中的类,经过判断最终返回的是SERVLET

同样,我们如果配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
经过查找和判断最终会得到REACTIVE。感兴趣的同学可以自己尝试,查看。
--------
下面探究一下
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
这两个方法是设置Application的所需实例和监听器,点击源码进入,最终获取的是META-INF/spring.factories的所有的配置项。
1 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { 2 MultiValueMap<String, String> result = cache.get(classLoader); 3 if (result != null) { 4 return result; 5 } 6 7 try { 8 Enumeration<URL> urls = (classLoader != null ? 9 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : 10 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); 11 result = new LinkedMultiValueMap<>(); 12 while (urls.hasMoreElements()) { 13 URL url = urls.nextElement(); 14 UrlResource resource = new UrlResource(url); 15 Properties properties = PropertiesLoaderUtils.loadProperties(resource); 16 for (Map.Entry<?, ?> entry : properties.entrySet()) { 17 String factoryTypeName = ((String) entry.getKey()).trim(); 18 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { 19 result.add(factoryTypeName, factoryImplementationName.trim()); 20 } 21 } 22 } 23 cache.put(classLoader, result); 24 return result; 25 } 26 catch (IOException ex) { 27 throw new IllegalArgumentException("Unable to load factories from location [" + 28 FACTORIES_RESOURCE_LOCATION + "]", ex); 29 } 30 }
1 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { 2 ClassLoader classLoader = getClassLoader(); 3 // Use names and ensure unique to protect against duplicates 4 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 5 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); 6 AnnotationAwareOrderComparator.sort(instances); 7 return instances; 8 }
上面代码低四行获取所有加载的jar包META-INF/spring.factories中配置的 ApplicationContextInitializer的类名,然后第五行创建实例。第六行对实现了org.springframework.core.annotation.Order实例进行排序
如下是找到的其中一个配置,根据所选组件的不同,所需加载的配置项也会有变化。


浙公网安备 33010602011771号