From 9d09acd880a7098d261da6320252ba04ce5bb581 Mon Sep 17 00:00:00 2001 From: senntyou Date: Fri, 22 Oct 2021 17:38:24 +0800 Subject: [PATCH] spring/11 --- README.md | 1 + spring/1.md | 4 +- spring/11.md | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 spring/11.md diff --git a/README.md b/README.md index ad17e30..23fc324 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ 1. [\[2021-10-21\] Spring 源码解析八:Xml 配置中默认的命名空间处理器](spring/8.md) 1. [\[2021-10-22\] Spring 源码解析九:默认的注解处理器](spring/9.md) 1. [\[2021-10-22\] Spring 源码解析十:请求参数注解解析器与响应值注解处理器](spring/10.md) +1. [\[2021-10-22\] Spring 源码解析十一:Spring 的扩展加载机制](spring/11.md) ## 前端进阶 diff --git a/spring/1.md b/spring/1.md index 7aab17a..5c7a26e 100644 --- a/spring/1.md +++ b/spring/1.md @@ -46,8 +46,8 @@ SpringMVC 框架的核心模块主要是:`spring-core`、`spring-beans`、`spr Spring bean 的加载与扩展机制有几点需要在这里简单介绍一下: 1. Spring bean 的定义主要是两种:基于注解的定义、基于 XML 文件的定义 -1. `spring-beans` 提供了基于 XML 配置的、第三方对 bean 的自定义扩展机制,主要是在 - `META-INF/spring.handlers, META-INF/spring.schemas` 文件中定义需要扩展的标签, +1. `spring-beans` 提供了基于 XML 配置的、第三方对 bean 的命令空间扩展机制,主要是在 + `META-INF/spring.handlers, META-INF/spring.schemas` 文件中定义需要扩展的命令空间, 比如 `, ` 1. 基于注解的自定义扩展,需要依赖 `spring-boot` 的扩展加载机制 diff --git a/spring/11.md b/spring/11.md new file mode 100644 index 0000000..8d8451d --- /dev/null +++ b/spring/11.md @@ -0,0 +1,261 @@ +# Spring 源码解析十一:Spring 的扩展加载机制 + +Spring 的扩展加载机制主要有 2 个:自动加载第三方包中的类、扩展 xml 配置文件中 bean 的命名空间 + +## 1. 自动加载第三方包中的类 + +`spring-core` 提供了一个类似 Java SPI 的的扩展机制,在 `META-INF/spring.factories` 文件中定义需要自动加载的类,就可以自动实例化其他包指定的类,`spring-boot, spring-cloud` 都依赖这个机制自动加载资源。 + +比如 `spring-boot` 的扩展(一部分) + +```properties +# Logging Systems +org.springframework.boot.logging.LoggingSystemFactory=\ +org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\ +org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\ +org.springframework.boot.logging.java.JavaLoggingSystem.Factory + +# PropertySource Loaders +org.springframework.boot.env.PropertySourceLoader=\ +org.springframework.boot.env.PropertiesPropertySourceLoader,\ +org.springframework.boot.env.YamlPropertySourceLoader + +# ConfigData Location Resolvers +org.springframework.boot.context.config.ConfigDataLocationResolver=\ +org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\ +org.springframework.boot.context.config.StandardConfigDataLocationResolver + +# ConfigData Loaders +org.springframework.boot.context.config.ConfigDataLoader=\ +org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\ +org.springframework.boot.context.config.StandardConfigDataLoader + +# Run Listeners +org.springframework.boot.SpringApplicationRunListener=\ +org.springframework.boot.context.event.EventPublishingRunListener +``` + +实现这个功能的类是 [SpringFactoriesLoader](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java) + +```java +public final class SpringFactoriesLoader { + // 自动加载文件地址 + public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + + // 加载spring.factories + public static List loadFactories(Class factoryType, @Nullable ClassLoader classLoader) { + ClassLoader classLoaderToUse = classLoader; + if (classLoaderToUse == null) { + classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); + } + // 加载spring.factories中的bean名字 + List factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); + + // 结果集 + List result = new ArrayList<>(factoryImplementationNames.size()); + for (String factoryImplementationName : factoryImplementationNames) { + // 初始化bean,并加入到结果集 + result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); + } + + return result; + } + + // 加载spring.factories中的bean名字 + public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) { + ClassLoader classLoaderToUse = classLoader; + if (classLoaderToUse == null) { + classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); + } + String factoryTypeName = factoryType.getName(); + // 加载spring.factories中的bean定义,并提取出名字 + return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); + } + + // 加载spring.factories中的bean定义 + private static Map> loadSpringFactories(ClassLoader classLoader) { + // 如果有缓存,直接返回缓存 + Map> result = cache.get(classLoader); + if (result != null) { + return result; + } + + // 结果集 + result = new HashMap<>(); + try { + // 加载所有包下的spring.factories,不光是主包,还有各种依赖包 + Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); + // 遍历读取 + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + UrlResource resource = new UrlResource(url); + // 把属性加载出来,以properties文件对待 + Properties properties = PropertiesLoaderUtils.loadProperties(resource); + for (Map.Entry entry : properties.entrySet()) { + // bean名字 + String factoryTypeName = ((String) entry.getKey()).trim(); + // bean类型,可以用逗号分隔多个 + String[] factoryImplementationNames = + StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); + // 遍历bean类型 + for (String factoryImplementationName : factoryImplementationNames) { + // 加入到结果集 + result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) + .add(factoryImplementationName.trim()); + } + } + } + + // 去重 + result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() + .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); + // 加入缓存 + cache.put(classLoader, result); + } + catch (IOException ex) { + // ... 代码省略 + } + return result; + } + + // 初始化bean + private static T instantiateFactory(String factoryImplementationName, Class factoryType, ClassLoader classLoader) { + try { + // 取出class + Class factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader); + // factoryImplementationClass不是factoryType的子类 + if (!factoryType.isAssignableFrom(factoryImplementationClass)) { + // 报错 + } + // 调用newInstance实例化 + return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance(); + } + catch (Throwable ex) { + // ... 代码省略 + } + } + +} +``` + +然后再 [CachedIntrospectionResults](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/beans/CachedIntrospectionResults.java) +静态加载 + +```java +public final class CachedIntrospectionResults { + private static final List beanInfoFactories = SpringFactoriesLoader.loadFactories( + BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); +} +``` + +## 2. 扩展 xml 配置文件中 bean 的命名空间 + +`spring-beans` 提供了基于 XML 配置的、第三方对 bean 的命令空间扩展机制,主要是在 +`META-INF/spring.handlers, META-INF/spring.schemas` 文件中定义需要扩展的命令空间, +如 `, ` + +比如 `spring-beans` 下的扩展 + +```properties +http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler +http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler +http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler +``` + +实现这个功能的类是 [DefaultNamespaceHandlerResolver](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java) + +```java +public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver { + // 自动加载文件地址 + public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers"; + + // 默认加载DEFAULT_HANDLER_MAPPINGS_LOCATION + public DefaultNamespaceHandlerResolver() { + this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION); + } + + // 默认加载DEFAULT_HANDLER_MAPPINGS_LOCATION + public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) { + this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); + } + + public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) { + this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); + this.handlerMappingsLocation = handlerMappingsLocation; + } + + // 解析命名空间 + @Override + public NamespaceHandler resolve(String namespaceUri) { + // 获取命令空间映射 + Map handlerMappings = getHandlerMappings(); + // 获取处理器 + Object handlerOrClassName = handlerMappings.get(namespaceUri); + + // 没有,返回null + if (handlerOrClassName == null) { + return null; + } + // NamespaceHandler,返回NamespaceHandler + else if (handlerOrClassName instanceof NamespaceHandler) { + return (NamespaceHandler) handlerOrClassName; + } + else { + String className = (String) handlerOrClassName; + try { + // 当做类加载 + Class handlerClass = ClassUtils.forName(className, this.classLoader); + // 初始化类,并调用init方法 + NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); + namespaceHandler.init(); + // 载入缓存 + handlerMappings.put(namespaceUri, namespaceHandler); + return namespaceHandler; + } + catch (ClassNotFoundException ex) { + // ... 代码省略 + } + // ... 代码省略 + } + } + + // 获取命令空间映射 + private Map getHandlerMappings() { + Map handlerMappings = this.handlerMappings; + // 如果已经加载过了,就不加载了 + if (handlerMappings == null) { + synchronized (this) { + handlerMappings = this.handlerMappings; + if (handlerMappings == null) { + try { + // 加载所有包下的spring.handlers,不光是主包,还有各种依赖包 + // 把属性加载出来,以properties文件对待 + Properties mappings = + PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); + + handlerMappings = new ConcurrentHashMap<>(mappings.size()); + CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); + + // 赋值给handlerMappings + this.handlerMappings = handlerMappings; + } + catch (IOException ex) { + // ... 代码省略 + } + } + } + } + return handlerMappings; + } +} +``` + +因为 `DefaultNamespaceHandlerResolver` 是默认的命名空间解析器,所以从一开始就会被初始化,所以也就会自动加载 `spring.handlers` + +## 后续 + +更多博客,查看 [https://github.com/senntyou/blogs](https://github.com/senntyou/blogs) + +作者:[深予之 (@senntyou)](https://github.com/senntyou) + +版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))