美文网首页
boot启动(二)

boot启动(二)

作者: 牧羊人刘俏 | 来源:发表于2019-12-16 16:57 被阅读0次

    上一篇讲到boot在启动的时候,会加载ApplicationContextInitializer和ApplicationListener这两种class的对象,那怎么加载呢。

    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();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    

    我现在才知道,可变成的参数,是可以啥都不传的,还以为必须要有个呢。
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    这个上下文加载器的梗后面再讲,毕竟我们看jvm里面的委托加载模型,貌似没有看到过这个啥
    ContextClassLoader。

    Set<String> names = new LinkedHashSet<>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    这个是将所有满足type的classname通过classLoader加载到set里面,后面再通过
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
    classLoader, args, names);的方式,将这些name加载到服务里面去,然后就可以使用了。
    ok,第一步,先找到名字
    Set<String> names = new LinkedHashSet<>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));

    /**
        在FACTORIES_RESOURCE_LOCATION下加载
     * Load the fully qualified class names of factory implementations of the
     * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
     * class loader.
     * @param factoryClass the interface or abstract class representing the factory
     * @param classLoader the ClassLoader to use for loading resources; can be
     * {@code null} to use the default
     * @throws IllegalArgumentException if an error occurs while loading factory names
     * @see #loadFactories
     */
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
    

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {

              //做了缓存,根据classLoader,等于说同一个classloader不会加载两遍这个信息
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
    
        try {
    

    //"META-INF/spring.factories" 在这个目录去找所有的配置信息
    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()) {
    String factoryClassName = ((String) entry.getKey()).trim();
    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    result.add(factoryClassName, factoryName.trim());
    }
    }
    }
    cache.put(classLoader, result);
    return result;
    }
    catch (IOException ex) {
    throw new IllegalArgumentException("Unable to load factories from location [" +
    FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    }

    如上,就是加载当前的classpath的路径META-INF/spring.factories下的所有的配置信息,我们随便的找一个看下,如下是boot下面的信息,典型的key value结构,value通过,来进行多value的配置。

    PropertySource Loaders

    org.springframework.boot.env.PropertySourceLoader=
    org.springframework.boot.env.PropertiesPropertySourceLoader,
    org.springframework.boot.env.YamlPropertySourceLoader

    Run Listeners

    org.springframework.boot.SpringApplicationRunListener=
    org.springframework.boot.context.event.EventPublishingRunListener

    Error Reporters

    org.springframework.boot.SpringBootExceptionReporter=
    org.springframework.boot.diagnostics.FailureAnalyzers

    Application Context Initializers

    org.springframework.context.ApplicationContextInitializer=
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
    org.springframework.boot.context.ContextIdApplicationContextInitializer,
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\

    ok,加载完成后
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
    通过factoryClassName找出key是factoryClassName的所有的配置信息,那我debug下我的boot启动的时候加载的信息。

    image.png

    上面是加载的所有的ApplicationContextInitializer的class,


    image.png

    上图是加载的所有的org.springframework.context.ApplicationListener信息。
    ok,name找到了,其次肯定就是loader进去了。
    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
    Class<?> instanceClass = ClassUtils.forName(name, classLoader);
    Assert.isAssignable(type, instanceClass);
    //拿到声明的构造方法,看来必须明确的声明才行
    Constructor<?> constructor = instanceClass
    .getDeclaredConstructor(parameterTypes);
    //反射操作,new出了一个实例,完美,加到了list里面去,就算完成了任务
    T instance = (T) BeanUtils.instantiateClass(constructor, args);
    instances.add(instance);
    }
    catch (Throwable ex) {
    throw new IllegalArgumentException(
    "Cannot instantiate " + type + " : " + name, ex);
    }
    }
    return instances;
    }

    接下来this.mainApplicationClass = deduceMainApplicationClass();
    这个方法还真是个有创意的写法,反正我是第一次见,
    private Class<?> deduceMainApplicationClass() {
    try {
    StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
    for (StackTraceElement stackTraceElement : stackTrace) {
    if ("main".equals(stackTraceElement.getMethodName())) {
    return Class.forName(stackTraceElement.getClassName());
    }
    }
    }
    catch (ClassNotFoundException ex) {
    // Swallow and continue
    }
    return null;
    }
    手动的抛一个异常出来,找到方法是main的方法,然后加载这个main方法对应的class,
    约定大于配置,main方法的名字不要乱用。
    ok ApplicationContextInitializer和ApplicationListener的信息加载完了,顺便MainApplicationClass的内容也找到了,接下来就是将springapplication run起来了。
    那留着第三张讲怎么run起来的吧。
    反正前两步都是在准备

    相关文章

      网友评论

          本文标题:boot启动(二)

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