Spring 源码解析十一:Spring 的扩大加载机制

Spring 的扩大加载机制次要有 2 个:主动加载第三方包中的类、扩大 xml 配置文件中 bean 的命名空间

1. 主动加载第三方包中的类

spring-core 提供了一个相似 Java SPI 的的扩大机制,在 META-INF/spring.factories 文件中定义须要主动加载的类,就能够主动实例化其余包指定的类,spring-boot, spring-cloud 都依赖这个机制主动加载资源。

比方 spring-boot 的扩大(一部分)

# Logging Systemsorg.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 Loadersorg.springframework.boot.env.PropertySourceLoader=\org.springframework.boot.env.PropertiesPropertySourceLoader,\org.springframework.boot.env.YamlPropertySourceLoader# ConfigData Location Resolversorg.springframework.boot.context.config.ConfigDataLocationResolver=\org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\org.springframework.boot.context.config.StandardConfigDataLocationResolver# ConfigData Loadersorg.springframework.boot.context.config.ConfigDataLoader=\org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\org.springframework.boot.context.config.StandardConfigDataLoader# Run Listenersorg.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.SimpleConstructorNamespaceHandlerhttp\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandlerhttp\://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 许可证)