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 许可证


深雨
12.7k 声望6.5k 粉丝

达则兼济天下,穷则独善其身。