Spring 源码解析十一:Spring 的扩展加载机制

x33g5p2x  于2022-02-11 发布在 Spring  
字(7.5k)|赞(0)|评价(0)|浏览(569)

Spring 的扩展加载机制主要有 2 个:自动加载第三方包中的类、扩展 xml 配置文件中 bean 的命名空间

1. 自动加载第三方包中的类

spring-core 提供了一个类似 Java SPI 的的扩展机制,在 META-INF/spring.factories 文件中定义需要自动加载的类,就可以自动实例化其他包指定的类,spring-boot, spring-cloud 都依赖这个机制自动加载资源。

比如 spring-boot 的扩展(一部分)

  1. # Logging Systems
  2. org.springframework.boot.logging.LoggingSystemFactory=\
  3. org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
  4. org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
  5. org.springframework.boot.logging.java.JavaLoggingSystem.Factory
  6. # PropertySource Loaders
  7. org.springframework.boot.env.PropertySourceLoader=\
  8. org.springframework.boot.env.PropertiesPropertySourceLoader,\
  9. org.springframework.boot.env.YamlPropertySourceLoader
  10. # ConfigData Location Resolvers
  11. org.springframework.boot.context.config.ConfigDataLocationResolver=\
  12. org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
  13. org.springframework.boot.context.config.StandardConfigDataLocationResolver
  14. # ConfigData Loaders
  15. org.springframework.boot.context.config.ConfigDataLoader=\
  16. org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
  17. org.springframework.boot.context.config.StandardConfigDataLoader
  18. # Run Listeners
  19. org.springframework.boot.SpringApplicationRunListener=\
  20. org.springframework.boot.context.event.EventPublishingRunListener

实现这个功能的类是 SpringFactoriesLoader

  1. public final class SpringFactoriesLoader {
  2. // 自动加载文件地址
  3. public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
  4. // 加载spring.factories
  5. public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
  6. ClassLoader classLoaderToUse = classLoader;
  7. if (classLoaderToUse == null) {
  8. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  9. }
  10. // 加载spring.factories中的bean名字
  11. List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
  12. // 结果集
  13. List<T> result = new ArrayList<>(factoryImplementationNames.size());
  14. for (String factoryImplementationName : factoryImplementationNames) {
  15. // 初始化bean,并加入到结果集
  16. result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
  17. }
  18. return result;
  19. }
  20. // 加载spring.factories中的bean名字
  21. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  22. ClassLoader classLoaderToUse = classLoader;
  23. if (classLoaderToUse == null) {
  24. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  25. }
  26. String factoryTypeName = factoryType.getName();
  27. // 加载spring.factories中的bean定义,并提取出名字
  28. return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
  29. }
  30. // 加载spring.factories中的bean定义
  31. private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  32. // 如果有缓存,直接返回缓存
  33. Map<String, List<String>> result = cache.get(classLoader);
  34. if (result != null) {
  35. return result;
  36. }
  37. // 结果集
  38. result = new HashMap<>();
  39. try {
  40. // 加载所有包下的spring.factories,不光是主包,还有各种依赖包
  41. Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
  42. // 遍历读取
  43. while (urls.hasMoreElements()) {
  44. URL url = urls.nextElement();
  45. UrlResource resource = new UrlResource(url);
  46. // 把属性加载出来,以properties文件对待
  47. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  48. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  49. // bean名字
  50. String factoryTypeName = ((String) entry.getKey()).trim();
  51. // bean类型,可以用逗号分隔多个
  52. String[] factoryImplementationNames =
  53. StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
  54. // 遍历bean类型
  55. for (String factoryImplementationName : factoryImplementationNames) {
  56. // 加入到结果集
  57. result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
  58. .add(factoryImplementationName.trim());
  59. }
  60. }
  61. }
  62. // 去重
  63. result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
  64. .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
  65. // 加入缓存
  66. cache.put(classLoader, result);
  67. }
  68. catch (IOException ex) {
  69. // ... 代码省略
  70. }
  71. return result;
  72. }
  73. // 初始化bean
  74. private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
  75. try {
  76. // 取出class
  77. Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
  78. // factoryImplementationClass不是factoryType的子类
  79. if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
  80. // 报错
  81. }
  82. // 调用newInstance实例化
  83. return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
  84. }
  85. catch (Throwable ex) {
  86. // ... 代码省略
  87. }
  88. }
  89. }

然后在 CachedIntrospectionResults
静态加载

  1. public final class CachedIntrospectionResults {
  2. private static final List<BeanInfoFactory> beanInfoFactories = SpringFactoriesLoader.loadFactories(
  3. BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader());
  4. }

2. 扩展 xml 配置文件中 bean 的命名空间

spring-beans 提供了基于 XML 配置的、第三方对 bean 的命令空间扩展机制,主要是在
META-INF/spring.handlers, META-INF/spring.schemas 文件中定义需要扩展的命令空间,
<dubbo:application name="name"/>, <dubbo:registry address="address"/>

比如 spring-beans 下的扩展

  1. http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
  2. http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
  3. http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

实现这个功能的类是 DefaultNamespaceHandlerResolver

  1. public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
  2. // 自动加载文件地址
  3. public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
  4. // 默认加载DEFAULT_HANDLER_MAPPINGS_LOCATION
  5. public DefaultNamespaceHandlerResolver() {
  6. this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
  7. }
  8. // 默认加载DEFAULT_HANDLER_MAPPINGS_LOCATION
  9. public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
  10. this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
  11. }
  12. public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) {
  13. this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
  14. this.handlerMappingsLocation = handlerMappingsLocation;
  15. }
  16. // 解析命名空间
  17. @Override
  18. public NamespaceHandler resolve(String namespaceUri) {
  19. // 获取命令空间映射
  20. Map<String, Object> handlerMappings = getHandlerMappings();
  21. // 获取处理器
  22. Object handlerOrClassName = handlerMappings.get(namespaceUri);
  23. // 没有,返回null
  24. if (handlerOrClassName == null) {
  25. return null;
  26. }
  27. // NamespaceHandler,返回NamespaceHandler
  28. else if (handlerOrClassName instanceof NamespaceHandler) {
  29. return (NamespaceHandler) handlerOrClassName;
  30. }
  31. else {
  32. String className = (String) handlerOrClassName;
  33. try {
  34. // 当做类加载
  35. Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  36. // 初始化类,并调用init方法
  37. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  38. namespaceHandler.init();
  39. // 载入缓存
  40. handlerMappings.put(namespaceUri, namespaceHandler);
  41. return namespaceHandler;
  42. }
  43. catch (ClassNotFoundException ex) {
  44. // ... 代码省略
  45. }
  46. // ... 代码省略
  47. }
  48. }
  49. // 获取命令空间映射
  50. private Map<String, Object> getHandlerMappings() {
  51. Map<String, Object> handlerMappings = this.handlerMappings;
  52. // 如果已经加载过了,就不加载了
  53. if (handlerMappings == null) {
  54. synchronized (this) {
  55. handlerMappings = this.handlerMappings;
  56. if (handlerMappings == null) {
  57. try {
  58. // 加载所有包下的spring.handlers,不光是主包,还有各种依赖包
  59. // 把属性加载出来,以properties文件对待
  60. Properties mappings =
  61. PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
  62. handlerMappings = new ConcurrentHashMap<>(mappings.size());
  63. CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
  64. // 赋值给handlerMappings
  65. this.handlerMappings = handlerMappings;
  66. }
  67. catch (IOException ex) {
  68. // ... 代码省略
  69. }
  70. }
  71. }
  72. }
  73. return handlerMappings;
  74. }
  75. }

因为 DefaultNamespaceHandlerResolver 是默认的命名空间解析器,所以从一开始就会被初始化,所以也就会自动加载 spring.handlers

后续

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证

相关文章