-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bc2e5b2
commit 9d09acd
Showing
3 changed files
with
264 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |