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为nullthis.resourceLoader = resourceLoader;//PrimarySources(即启动类)一定不能为nullAssert.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) {//获取当前使用的ClassLoaderClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {//为空,则获取SpringFactoriesLoader的类加载器classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}//org.springframework.boot.BootstrapRegistryInitializerString 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;}//缓存中没有,初始化resultresult = 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();//构建UrlResourceUrlResource 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都置为包含唯一元素的不可修改的listresult.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类型,向下,否则抛异常IllegalArgumentExceptionAssert.isAssignable(type, instanceClass);//获取instanceClass的构造器Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);//通过BeanUtils的instantiateClass()方法实例化类。T instance = (T) BeanUtils.instantiateClass(constructor, args);//将实例化出来的类放入instancesinstances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;}