SpringBoot启动类的代码底层原理第一步

首先我们来看一段代码,SpringBoot启动类:

public static void main(String[] args) {          SpringApplication.run(StudentApplication.class);}

只有一行代码,却完成了ssm中大量配置才能完成的事情,这一行代码中的一个方法究竟做了什么事情。

 

在我们该类之后,大致做了两件事,一是SpringApplication初始化,二是SpringApplication.run()启动

 

public static ConfigurableApplicationContext run(Class<?> primarySource,      String... args) {
      return run(new Class<?>[] { primarySource }, args);  }

 

调用静态方法初始化SpringApplication,然后启动运行

public static ConfigurableApplicationContext run(Class<?>[] primarySources,      String[] args) {
      return new SpringApplication(primarySources).run(args);  }

 

初始化方法底层代码

public SpringApplication(Class<?>... primarySources) {
      this(null, primarySources);  }   public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
      //初始化资源加载器    this.resourceLoader = resourceLoader;    Assert.notNull(primarySources, "PrimarySources must not be null");    //将启动类的配置信息存储在集合中    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));    //推断web环境的类型(reactive,none,servlet)    this.webApplicationType = deduceWebApplicationType();    //设置初始化器 从配置文件spring.factories中查找所有的key=org.springframework.context.ApplicationContextInitializer的类【加载,初始化,排序】    setInitializers((Collection) getSpringFactoriesInstances(        ApplicationContextInitializer.class));    //设置*** 从配置文件spring.factories中查找所有的key=org.springframework.context.ApplicationListener的类.【加载,初始化,排序】    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));    //推断main方法定义的类    this.mainApplicationClass = deduceMainApplicationClass();  }

 

1.初始化资源加载器

 

this.resourceLoader = resourceLoader;

 

 

2.将启动类的信息存储在集合中

 

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

 

 

3.推断web环境的类型(reactive,none,servlet)

private WebApplicationType deduceWebApplicationType() {
      if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)        && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;    }    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
          return WebApplicationType.NONE;      }    }    return WebApplicationType.SERVLET; }

 

 

4.设置初始化器

 

setInitializers((Collection) getSpringFactoriesInstances(        ApplicationContextInitializer.class));

底层代码

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,      Class<?>[] parameterTypes, Object... args) {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();    // Use names and ensure unique to protect against duplicates   // 使用Set保存names来避免重复元素    Set<String> names = new LinkedHashSet<>(        SpringFactoriesLoader.loadFactoryNames(type, classLoader));    // 通过上面获取到的类的全限定名,这里将会使用Class.forName加载类,并调用构造方法实例化类    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,        classLoader, args, names);    // 对实例进行排序    AnnotationAwareOrderComparator.sort(instances);    return instances;  }      public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";        //类方法加载的过程  public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
      String factoryClassName = factoryClass.getName();    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());  }
  private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
      MultiValueMap<String, String> result = cache.get(classLoader);    if (result != null) {
        return result;    }
    try {
      //从类路径的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()) {
            List<String> factoryClassNames = Arrays.asList(              StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));          result.addAll((String) entry.getKey(), factoryClassNames);        }      }      cache.put(classLoader, result);      return result;    }    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +          FACTORIES_RESOURCE_LOCATION + "]", ex);    }  }

 

 

META-INF/spring.factories,如图示:

 

 

 

执行完之后:

 

 

5.设置***

 

  •  
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

 

具体的方式和上面初始化器相同。

 

 

6.推断mian()自定义的类

 

  •  
this.mainApplicationClass = deduceMainApplicationClass();

底层

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
private Class<?> deduceMainApplicationClass() {
      try {
       //获取栈帧      StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();      for (StackTraceElement stackTraceElement : stackTrace) {
          // 通过main的栈帧推断出入口类的名字        if ("main".equals(stackTraceElement.getMethodName())) {
            return Class.forName(stackTraceElement.getClassName());        }      }    }    catch (ClassNotFoundException ex) {
        // Swallow and continue    }    return null;  }

获取栈帧

 

循环遍历得到main()并推断出入口类的名字

 

 

代码执行完成之后

 

全部评论

相关推荐

吴offer选手:网易这个双机位麻烦死了
投递网易等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务