大致介绍 SpringFactoriesLoader是 spring 的一个核心类,在 spring-core.jar 包中。它的作用是,用ClassLoader找出项目依赖jar包中的所有“META-INF/spring.factories”文件,将里面的内容加载成 Properties对象(key value对,一个文件加载出一个 Properties),再由 Properties 对象转为 Map<String, List> 格式。
以下面这个文件为例:
1 2 3 4 5 6 7 8 9 10 11 12 spring-boot-autoconfigure-2.3.0.RELEASE.jar META-INF/spring.factories # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer 。。。。。
这个文件加载出的 Properties 对象内容有:
1 2 3 4 5 6 7 key1 = "org.springframework.context.ApplicationContextInitializer" value1 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener" key2 = "org.springframework.context.ApplicationListener" value2 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer" 等等
最后转成的 Map<String, List> 格式内容为:
1 2 3 4 5 6 7 8 key1 = "org.springframework.context.ApplicationContextInitializer" value1List = ["org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer","org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"] -- 这个 value1List 其实就是把 Properties 对象的 value 保存的字符串按逗号分隔变成的数组 key2 = "org.springframework.context.ApplicationListener" value2List = ["org.springframework.boot.autoconfigure.BackgroundPreinitializer"] 等等
扫描这个“META-INF/spring.factories”文件的目的是什么? SpringBoot 默认会扫描并管理在启动类包路径下的 Bean。如果有想要被扫描但不想改变默认扫描的路径,比如自己写的 jar 包中有 Bean,但不要求使用这个 jar 的应用必须修改默认扫描路径,就在自己 jar 中添加这个“META-INF/spring.factories”文件,将 jar 中的 Bean 的接口与实现类写上,SpringBoot 启动时会在某个阶段?将这些Bean扫描并管理。
在哪些路径下寻找spring.factories文件 SpringFactoriesLoader.loadSpringFactories方法中的下面这段代码:
1 2 3 Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
其中 classLoader,即类加载器,是这个方法的入参,可以为null,为null时使用“系统类加载器”,即“应用程序类加载器”,常量 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”。
这段代码通过调用classLoader.getResources方法返回一个CompoundEnumeration<URL>对象,元素URL中保存的就是某一个“spring.factories”文件所在的绝对路径,如“file:/D:/Development/maven-repo/org/springframework/boot/spring-boot/2.3.0.RELEASE/spring-boot-2.3.0.RELEASE.jar!/META-INF/spring.factories”。但是,在这行代码中还不会得到“spring.factories”文件所在的绝对路径,可以认为这时返回的CompoundEnumeration<URL>对象中的URL元素是空的,它只是获取出会在哪些路径下寻找 URL。
会在哪些路径下寻找 URL 呢?研究ClassLoader.getResources源码就会知道,这些路径就是 启动类加载器+扩展类加载器+应用程序类加载器 3个类加载器负责的路径的并集,即”JVM系统参数sun.boot.class.path” + “JVM系统参数java.ext.dirs” + “环境变量classpath” 包含的路径的并集!也就是说,SpringFactoriesLoader 将会去启动类加载器、扩展类加载器、应用程序类加载器它们3个负责加载的全部路径中,寻找“META-INF/spring.factories”文件,只不过,这个文件一般只存在于应用程序类加载器负责的路径,即“环境变量classpath”指定的路径下。
上面说到,从classLoader.getResources方法返回的变量中还没有找到“spring.factories”文件所在的绝对路径,每个文件的绝对路径是在后面的代码中获取的(代码解释见注释):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } }
loadFactoryNames 方法 已知loadSpringFactories方法返回的 Map<String, List> result 中,key为接口全限定名,value为“spring.factories”文件中配置的该接口的实现类全限定名。
loadFactoryNames方法是根据入参Class<?> factoryType指定的 className(接口名),从 loadSpringFactories 方法返回的 Map 中取该接口名对应的实现类名 List。
代码最后的 getOrDefault 意思是如果返回的 Map 中没有指定 key 对应的 value,那么就返回 value=空List,如果有就返回真正的 value。
1 2 3 4 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
loadFactories 方法 这个方法首先通过loadFactoryNames方法获取指定接口的实现类名 List(变量 factoryImplementationNames),再用instantiateFactory方法根据这些实现类名反射创建 这些实现类的实例对象,如果这些实现类有使用PriorityOrdered或Ordered,还会根据其标注的顺序排序,最后返回排序后的实现类实例对象 List。
排序方法 AnnotationAwareOrderComparator :
用于比较的两个对象都实现了 PriorityOrdered 接口,对比它们的 getOrder() 的值,值小的在前
两个对象中只有一个实现了 PriorityOrdered 接口,则实现了这个接口的排在前
两个对象中分别查找它们是否实现了Ordered 接口,是则获取 getOrder() 值;若未实现Ordered 接口,就查看它们是否标注了 Order 注解,是则获取注解中的排序值;若也没有 Order 注解,再看是否有 Priority 注解,有则获取注解中的排序值;都没有则返回一个大值,则这个类会被排在后面。排序值小的排在前
当两个对象排序值相同,会按对象名称排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static <T> List<T> loadFactories (Class<T> factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null" ); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class .getClassLoader () ; } List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } List<T> result = new ArrayList<>(factoryImplementationNames.size()); for (String factoryImplementationName : factoryImplementationNames) { result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; } private static <T> T instantiateFactory (String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) { try { Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader); if (!factoryType.isAssignableFrom(factoryImplementationClass)) { throw new IllegalArgumentException( "Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]" ); } return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException( "Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]" , ex); } }