写点什么

【SpringBoot 二】spring.factories 加载时机分析

  • 2022 年 8 月 04 日
  • 本文字数:2065 字

    阅读完需:约 7 分钟

【SpringBoot 二】spring.factories加载时机分析

1、spring.factories 作用


这个类似于 Java 中的 SPI 功能,SpringBoot 启动的时候会读取所有 jar 包下面的META-INF/spring.factories文件; 并且将文件中的 接口/抽象类 对应的实现类都对应起来,并在需要的时候可以实例化对应的实现类

下面我们来分析一下源码看看spring.factories的使用场景

2、源码解析


启动 SpringApplication,看看构造方法

 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {  this.resourceLoader = resourceLoader;  Assert.notNull(primarySources, "PrimarySources must not be null");  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));  this.webApplicationType = WebApplicationType.deduceFromClasspath();  setInitializers((Collection) getSpringFactoriesInstances(    ApplicationContextInitializer.class));  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));  this.mainApplicationClass = deduceMainApplicationClass(); }
复制代码

其中方法getSpringFactoriesInstances( ApplicationContextInitializer.class) 是用于获取 Spring 中指定类实例用的;并且获取的时候是根据读取整个项目中文件路径为META-INF/spring.factories 中的内容实例化对应的实例类的;

例如这里的ApplicationContextInitializer 是一个接口,那么应该实例化哪些他的实现类呢?那就找META-INF/spring.factories文件 ; 那么我们在spring-boot:2.1.0jar 包中找到了这个文件

读取到需要实例化的实现类为


org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
复制代码

并且还在spring-boot-autoconfigure-2.1.0.RELEASE.jar中找到了这个文件


那么文件中的两个实现类也会被实例化;加上上面 4 个总共有 6 个

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
复制代码


可以看到不仅仅只是把org.springframework.context.ApplicationContextInitializer的实例类解析了出来;而是所有的都解析了出来并且保存下来了.下次其他的类需要被实例化的时候就可以直接从内存里面拿了;

上面过程拿到了实例类之后,接下来就是实例化的过程了

 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,   Class<?>[] parameterTypes, Object... args) {  ClassLoader classLoader = getClassLoader();  // Use names and ensure unique to protect against duplicates  Set<String> names = new LinkedHashSet<>(    SpringFactoriesLoader.loadFactoryNames(type, classLoader));  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,    classLoader, args, names);  AnnotationAwareOrderComparator.sort(instances);  return instances; }


复制代码

方法createSpringFactoriesInstances就是创建实例的过程;可以看到传入了对应的接口类org.springframework.context.ApplicationContextInitializer;接下来就会实例化 上面找到了对应的实现类;

 private <T> List<T> createSpringFactoriesInstances(Class<T> type,   Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,   Set<String> names) {  List<T> instances = new ArrayList<>(names.size());  for (String name : names) {   try {    Class<?> instanceClass = ClassUtils.forName(name, classLoader);    Assert.isAssignable(type, instanceClass);    Constructor<?> constructor = instanceClass      .getDeclaredConstructor(parameterTypes);    T instance = (T) BeanUtils.instantiateClass(constructor, args);    instances.add(instance);   }   catch (Throwable ex) {    throw new IllegalArgumentException(      "Cannot instantiate " + type + " : " + name, ex);   }  }  return instances; }
复制代码

实例化的过程如果,没有什么特别需要讲解的;

上面有个方法AnnotationAwareOrderComparator.sort(instances);是用来排序所有实例的; 实现类需要实现 接口Ordered ; getOrder返回的值越小,优先级更高

用法

知道spring.factories的用法之后, 那么我们就可以利用这个特性实现自己的目的;例如我们也可以写一个接口类ApplicationContextInitializer的实现类;等等之类的;

在这里插入图片描述

发布于: 刚刚阅读数: 3
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019.09.06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 也可获取《kafka运维大全》

评论

发布
暂无评论
【SpringBoot 二】spring.factories加载时机分析_springboot_石臻臻的杂货铺_InfoQ写作社区