【001】SpringBoot2 启动流程分析一(源码版本2.0.5.RELEASE)

1.入口:

@SpringBootApplication
public class BusApplication {

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

 通过调用

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) 静态方法

主要执行线索大的方向有两条:

(new SpringApplication(primarySources)).run(args);

以上代码拆分为两步走:

    1. SpringApplication 对象的创建

new SpringApplication(primarySources)

  2.调用SpringApplication对象的run方法

  后续会根据这两方面分析。

2. SpringApplication 对象的创建

主要代码流程如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
   //推断应用类型
    this.webApplicationType = this.deduceWebApplicationType();
    //setInitializers初始化模块,加载ApplicationContextInitializer
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //加载监听器
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
   // 推断mian方法所在类【此地有彩蛋----通过】后续会分析 通过new RuntimeException()获取到栈信息
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

  2.1 推断应用类型分析

this.webApplicationType = this.deduceWebApplicationType();

    枚举类定义一共三种类型:

public enum WebApplicationType {
    NONE,
    SERVLET,
    REACTIVE;

    private WebApplicationType() {
    }
}

 此方法中重点调用了

ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader)

后续会专门分析,此处不表,此类做了大量优化

2.2 setInitializers初始化模块,加载ApplicationContextInitializer

重点分析:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

阅读Spring Boot代码会发现大量调用此方法,这个就是Spring 借鉴 JAVA的SPI机制

SPI后续会专门分析

getSpringFactoriesInstances(ApplicationContextInitializer.class)

具体步骤分析:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//1获取类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

重点分析:

SpringFactoriesLoader.loadFactoryNames(type, classLoader)

源码如下:

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();

    try {
        Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
        ArrayList result = new ArrayList();

        while(urls.hasMoreElements()) {
            URL url = (URL)urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }

        return result;
    } catch (IOException var8) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    }
}

这个才是重点;

META-INF/spring.factories

扫描jar包下面的META-INF下面的spring.factories文件

循环处理spring.factories文件

1.读取spring.factories文件内容

2.将文件转化为Properties对象 (此对象和我们熟知的MAP差不多都是键值对)

3.根据接口的class名称 获取实现该接口的对象名称字符串

4.根据 逗号分隔字符串 ,然后转化为ArrayList

开始创建对象实例:

List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
      classLoader, args, names);

源码如下:

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
      Set<String> names) {
// 1.根据接口实现的对象列表的大小创建List集合
   List<T> instances = new ArrayList<T>(names.size());
//2.开始循环处理
   for (String name : names) {
      try {
         //3.获取某个实现类的 Class对象
         Class<?> instanceClass = ClassUtils.forName(name, classLoader);
         Assert.isAssignable(type, instanceClass);
         //4.根据Class对象,根据参数类型,获取响应构造函数
         Constructor<?> constructor = instanceClass
               .getDeclaredConstructor(parameterTypes);
           //5.通过构造函数,反射获取对象实例
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
         instances.add(instance);
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException(
               "Cannot instantiate " + type + " : " + name, ex);
      }
   }
   return instances;
}

上述的过程在Spring框架中有众多的应用。

例如:

ApplicationContextInitializer.class
ApplicationListener.class 

均是采用如上方法实例化的;

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值