Skip to content

Commit

Permalink
spring/11
Browse files Browse the repository at this point in the history
  • Loading branch information
deepraining committed Oct 22, 2021
1 parent bc2e5b2 commit 9d09acd
Show file tree
Hide file tree
Showing 3 changed files with 264 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

## 前端进阶

Expand Down
4 changes: 2 additions & 2 deletions spring/1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 文件中定义需要扩展的命令空间
比如 `<dubbo:application name="name"/>, <dubbo:registry address="address"/>`
1. 基于注解的自定义扩展,需要依赖 `spring-boot` 的扩展加载机制

Expand Down
261 changes: 261 additions & 0 deletions spring/11.md
Original file line number Diff line number Diff line change
@@ -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 <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 加载spring.factories中的bean名字
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);

// 结果集
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
// 初始化bean,并加入到结果集
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}

return result;
}

// 加载spring.factories中的bean名字
public static List<String> 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<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 如果有缓存,直接返回缓存
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}

// 结果集
result = new HashMap<>();
try {
// 加载所有包下的spring.factories,不光是主包,还有各种依赖包
Enumeration<URL> 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> T instantiateFactory(String factoryImplementationName, Class<T> 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<BeanInfoFactory> beanInfoFactories = SpringFactoriesLoader.loadFactories(
BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader());
}
```

## 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` 下的扩展

```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<String, Object> 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<String, Object> getHandlerMappings() {
Map<String, Object> 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)

0 comments on commit 9d09acd

Please sign in to comment.