SpringApplication对象的构建及spring.factories的加载时机
构建SpringApplication对象源码:
1、调用启动类的main()方法,该方法中调用SpringApplication的run方法。
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
2、调用SpringApplication的run()方法的重载方法,在发方法内构建了SpringApplication对象
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
3、构建SpringApplication对象。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//resourceLoader为null
this.resourceLoader = resourceLoader;
//PrimarySources(即启动类)一定不能为null
Assert.notNull(primarySources, "PrimarySources must not be null");
//初始化primarySources属性
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//从Classpath中推断Web应用类型。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
构建过程:
接下来我们来详细研究一下上述过程。在构建SpringApplication对象过程中,此时resourceLoader
为null,primarySources一定不为空且需要初始化为启动类SpringbootdemoApplication
。从Classpath中推断出Web应用类型并初始化webApplicationType,通过getSpringFactoriesInstances()获取Spring工厂实例来初始化bootstrapRegistryInitializers,initializers(List<ApplicationContextInitializer<?>>应用程序上下文初始化器列表), listeners(List
4、从Classpath推断Web应用类型,调用的是WebApplicationType类的deduceFromClasspath()方法。如果Classpath中包含DispatcherHandler,则表明当前WebApplicationType为REACTIVE;如果既不包含javax.servlet.Servlet也不包含Spring中的ConfigurableWebApplicationContext,则表明当前WebApplicationType为NONE; 上述两种都不是则表明当前WebApplicationType是SERVLET
static WebApplicationType deduceFromClasspath() {
// 如果org.springframework.web.reactive.DispatcherHandler的class文件存在且可以加载
//不存在org.springframework.web.servlet.DispatcherServlet
//不存在org.glassfish.jersey.servlet.ServletContainer
//则返回REACTIVE表明 该应用程序应作为响应式Web应用程序运行,并应启动嵌入式servlet Web 服务器
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//遍历SERVLET 指标类
//如果不存在javax.servlet.Servlet也不存在org.springframework.web.context.ConfigurableWebApplicationContext
//则返回NONE表明该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
//返回SERVLET表明该应用程序应作为基于servlet的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器。
return WebApplicationType.SERVLET;
}
spring.factories的加载时机
1、在构建SpringApplication中,初始化其属性bootstrapRegistryInitializers属性时进行加载 /META-INF/spring.factories。
2、构建SpringApplication对象时,通过调用getSpringFactoriesInstances(Class type)获取工厂实例。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
......
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
......
}
3、我们以初始化bootstrapRegistryInitializers为例讲解,getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)中首先获取ClassLoader ,通过SpringFactoriesLoader机制,根据ClassLoader从类路径jar包中加载META-INF/spring.factories下的所有工厂类及其实现类 。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//获取ClassLoader
ClassLoader classLoader = getClassLoader();
// 使用SpringFactoriesLoader机制加载出工厂名,并放入Set集合中确保其唯一性。但是在META-INF/spring.factories中并无BootstrapRegistryInitializer所以此处的names的size为0。
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建Spring工厂实例(但因为上述names的size为0,所以对于BootstrapRegistryInitializer来说它并不会创建SpringFactory实例)
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//通过AnnotationAwareOrderComparator对Spring工厂实例排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
4、SpringFactoriesLoader中的loadFactoryNames来加载META-INF/spring.factories下的所有工厂类及其实现类
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//获取当前使用的ClassLoader
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
//为空,则获取SpringFactoriesLoader的类加载器
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//org.springframework.boot.BootstrapRegistryInitializer
String factoryTypeName = factoryType.getName();
//加载META-INF/spring.factories下的扩展类,没有值的返回一个空列表。
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
5、重点关注SpringFactoriesLoader中的loadSpringFactories(ClassLoader classLoader),该方法具体实现了从META-INF/spring.factories下加载扩展类。
//工厂类所在位置,在多个jar文件中都有。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//检查缓存中是否有工厂类
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
//缓存中没有,初始化result
result = new HashMap<>();
try {
//加载META-INF/spring.factories下的一系列元素
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
//迭代遍历
while (urls.hasMoreElements()) {
//spring-boot-版本号.jar文件中Spring.factories所在的绝对路径
URL url = urls.nextElement();
//构建UrlResource
UrlResource resource = new UrlResource(url);
//加载该UrlResource中的属性(即Spring.factories中的键值对)
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//父类工厂类型名
String factoryTypeName = ((String) entry.getKey()).trim();
//子类工厂实现类名数组(在Spring.factories中多个用逗号分隔)
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
//将工厂类型名,工厂实现类列表放入名为result的 Map<String, List<String>>中,key为工厂类型名,value为工厂实现类列表。
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
//将result中的所有list都置为包含唯一元素的不可修改的list
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
//放入缓存。
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
bootstrapRegistryInitializers 的初始化中实现了加载META-INF/spring.factories中工厂扩展类(但是在META-INF/spring.factories并无bootstrapRegistryInitializers ),并将其放入缓存Map<String, List>,其中key为父类工厂名,value为其对应扩展类列表。之后initializers(ApplicationContextInitializer列表)以及listeners(ApplicationListener列表)的初始化都是从该缓存中获取值。
6、接下里我们看一下第三步中的createSpringFactoriesInstances()方法。由于bootstrapRegistryInitializers 在META-INF/spring.factories中并不存在。所以我们只有它返回的instances是空的即它不会创建SpringFactoriesInstances。但是初始化initializers,listeners时,却会。我们看一下具体源码。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
//初始化names.size()大小的list存放创建出来的实例。
List<T> instances = new ArrayList<>(names.size());
//遍历传入的工厂扩展类实例名
for (String name : names) {
try {
//通过反射获取instance的Class对象
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
//instanceClass是一个type类型,向下,否则抛异常IllegalArgumentException
Assert.isAssignable(type, instanceClass);
//获取instanceClass的构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//通过BeanUtils的instantiateClass()方法实例化类。
T instance = (T) BeanUtils.instantiateClass(constructor, args);
//将实例化出来的类放入instances
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}