美文网首页Spring 学习
04--SpringBoot启动之初始化与扩展点加载机制

04--SpringBoot启动之初始化与扩展点加载机制

作者: 闲来也无事 | 来源:发表于2018-08-19 00:10 被阅读231次

了解了SpringBoot启动类型判断之后,接着分析SpringBoot一些内部组件的初始化过程

//初始化Initializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化Listener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

Dubbo中有通过ExtensionLoader来实现扩展点加载的机制,在SpringBoot中也有类似的实现,降低耦合

1. 扩展点加载

//扩展点加载
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[]{});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    //获取当前线程上下文类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    //获取到的扩展类名存入set集合防止重复
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //创建扩展点实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    //排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

加载扩展点类名
SpringFactoriesLoader.loadFactoryNames(type, classLoader)

//通过给定类加载器获得完全限定类名
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        //获得factoryClass名称
        String factoryClassName = factoryClass.getName();
        //加载类并返回默认集合
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    List<String> factoryClassNames = Arrays.asList(
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                    result.addAll((String) entry.getKey(), factoryClassNames);
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

SpringBoot的扩展点存放在哪里呢,答案就在FACTORIES_RESOURCE_LOCATION静态变量中
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

image.png
classLoader会加载当前工程所有jar包(并非只有spring-boot一个工程)/META-INF/spring.factories文件,并从中找出要被加载的类,我们来看下ApplicationContextInitializer.class类在/META-INF/spring.factories对应的信息,可以看到.factories文件的存储方式为key:val0,val1,val2...
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
#用来报告Spring容器的一些常见的错误配置的
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
#获取应用上下文ID
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
#委托给context.initializer.classes环境属性下指定的其他初始值设定项。
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
#ApplicationContextInitializer设置服务器实际侦听Environment的端口的属性EmbeddedServletContainer。属性“local.server.port”可以直接注入使用 @Value或通过Environment。
#如果EmbeddedWebApplicationContext有一个 namespace集合,它将用于构造属性名称。例如,“管理”执行器上下文将具有属性名称“local.management.port”。属性会自动传播到任何父上下文。
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

查看debug堆栈信息

image.png
除了spring-boot工程下的/META-INF/spring.factoriesApplicationContextInitializer被加载之外,还有org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializerorg.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer被加载,这两个类信息位于spring-boot-autoconfigure工程的/META-INF/spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

至此,setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));所需要的类信息已经全部加载

2.扩展点实例化

//创建扩展点实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

//实例化扩展点,获取与参数对应的构造方法,并实例化
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
        try {
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        } catch (Throwable ex) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));初始化监听器的方法与上述步骤相同
至此,我们已经了解了SpringBoot的一些扩展点是如何加载并实例化的,并且完成了SpringApplication的实例化工作

相关文章

  • 04--SpringBoot启动之初始化与扩展点加载机制

    了解了SpringBoot启动类型判断之后,接着分析SpringBoot一些内部组件的初始化过程 Dubbo中有通...

  • dubbo的spi机制

    dubbo的spi机制 dubbo的扩展点加载机制源自于java的spi扩展机制。那么,何为java的spi扩展机...

  • JVM性能调优

    JVM类加载机制详解 从JDK源码级别深度剖析加载全过程 启动类、扩展类、应用程序类加载器源码深度剖析 类加载双亲...

  • Android 类加载机制以及基于类加载机制的热修复

    android 与 java 的类加载器 java 类加载机制 BootStrapClassLoader:启动类加...

  • HiveMQ源码阅读

    插件加载及启动 同步加载异步启动 启动后校验 数据持久化加载及初始化 异步线程池加载及初始化 客户端连接流程鉴权 ...

  • ExtensionLoader解析

    版本 2.5.7 ExtensionLoader机制 Dubbo 的扩展点加载从 JDK 标准的 SPI (Ser...

  • dubbo中的SPI扩展机制

    dubbo官网对其SPI扩展机制的介绍如下: Dubbo 的扩展点加载从 JDK 标准的 SPI (Service...

  • 理解类加载的双亲委派模型

    类加载器 启动类加载器 BootstrapClassLoader 扩展类加载器 ExtensionClassLoa...

  • JVM学习(二)类加载器

    目录 一、类加载器 还记得类加载机制吗?类加载机制的各阶段是加载、连接(验证、准备、解析)、初始化、使用、卸载。可...

  • 【Java基础】类加载过程

    要点:1、类加载机制的原理2、程序初始化的顺序3、类加载的代理模式(双亲委托机制) 一、类加载机制 JVM把cla...

网友评论

    本文标题:04--SpringBoot启动之初始化与扩展点加载机制

    本文链接:https://www.haomeiwen.com/subject/ixvqiftx.html