0
点赞
收藏
分享

微信扫一扫

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


spring.factories作用


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


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

源码解析

启动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.0​​jar包中找到了这个文件

【SpringBoot 二】spring.factories加载时机分析_实例化

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

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​​中找到了这个文件

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

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

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

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

可以看到不仅仅只是把​​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​​的实现类;等等之类的;

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



举报

相关推荐

0 条评论