暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SpringApplication构造方法分析

460



Hi~朋友,关注置顶防止错过消息



启动Spring Boot应用

作为一个SpringBoot应用,我们通常会用以下代码启动我们的应用程序,主要分为两个步骤:

  • • 初始化SpringApplication对象

  • • 调用SpringApplication的run方法

public static void main(String[] args) {
    SpringApplication application = new SpringApplication(DaemonApplication.class);
    application.run(args);
}

SpringApplication构造方法

我们会首先创建一个SpringApplication实例,我们可以看一下SpringApplication的构造方法主要做了哪些事情。

    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = new ArrayList<>(
                getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

在构造方法中主要会做以下操作:

  • • 推测运行环境

  • • 初始化ApplicationContextInitializer

  • • 初始化BootstrapRegistryInitializer

  • • 初始化ApplicationListener

  • • 设置主启动类

其中加载和初始化对象主要依赖于getSpringFactoriesInstances

getSpringFactoriesInstances

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // 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;
    }

SpringFactoriesLoader.loadFactoryNames主要用来找到需要初始化的一些class。分为两个步骤:

  • • 通过SpringFactoriesLoader.loadFactoryNames找到所有的需要创建对象的Class

  • • 通过createSpringFactoriesInstances创造指定的对象

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 {
            Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    String[] factoryImplementationNames =
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                .add(factoryImplementationName.trim());
                    }
                }
            }

            // Replace all lists with unmodifiable lists containing unique elements
            result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            cache.put(classLoader, result);
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
        return result;
    }

在该方法中主要做以下事情:

  • • 通过ClassLoader.getResources加载所有的META-INF/spring.factories文件

  • • 通过PropertiesLoaderUtils.loadProperties加载单个META-INF/spring.factories文件中的所有属性

  • • 将加载到的属性全部保存到result中,同时将该result缓存到ConcurrentReferenceHashMap中,方便下次直接获取

在spring-boot-2.7.7中,ApplicationContextInitializer和ApplicationListener主要有以下实例:

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer


# Application Listeners

org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

deduceMainApplicationClass方法

deduceMainApplicationClass用来设置主启动类,主要是通过函数调用栈来找到main方法,main方法所在的class就是启动类。

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;
}

本期文章就到这,扫码关注,更多内容我们下期再见!



文章转载自程序员修炼笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论