Spring 源码解析十一:Spring 的扩大加载机制
Spring 的扩大加载机制次要有 2 个:主动加载第三方包中的类、扩大 xml 配置文件中 bean 的命名空间
1. 主动加载第三方包中的类
spring-core
提供了一个相似 Java SPI 的的扩大机制,在 META-INF/spring.factories
文件中定义须要主动加载的类,就能够主动实例化其余包指定的类,spring-boot, spring-cloud
都依赖这个机制主动加载资源。
比方 spring-boot
的扩大(一部分)
# 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
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
动态加载
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
下的扩大
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
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
作者:深予之 (@senntyou)
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)