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

281 篇文章 0 订阅
281 篇文章 0 订阅

首先我们来看一段代码,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()并推断出入口类的名字

​代码执行完成之后

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值