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);
    }
}
View Code

点击进入

    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     }
View Code

 

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实例进行排序

 如下是找到的其中一个配置,根据所选组件的不同,所需加载的配置项也会有变化。

 

posted @ 2020-03-13 15:48  wjwei  阅读(598)  评论(0)    收藏  举报