4.SpringApplication.run方法学习

发布时间 2023-06-04 15:29:05作者: Kavins
/**
     * SpringApplication.run方法
     */
    public ConfigurableApplicationContext run(String... args) {
        // 用来记录启动时间的类
        StopWatch stopWatch = new StopWatch();
        // 记录启动时间
        stopWatch.start();
        // 定义变量
        ConfigurableApplicationContext context = null;
        // 创建一个异常报告集合
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 配置headless属性为true(服务器没有显示屏什么的,
        // 你得告诉程序一声,你工作的地方没有这些设备。如果需要,自己模拟)
        configureHeadlessProperty();
        // 获取运行监听器,这里获取到的是EventPublishingRunListener
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 调用监听器的开始方法,内部会批量的调用监听器(EventPublishingRunListener)的starting方法,
        // 以发送事件等来间接调用ApplicationListener的onApplicationEvent方法
        listeners.starting();
        try {
            // 创建参数类
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            // 跳过对BeanInfo的搜索
            configureIgnoreBeanInfo(environment);
            // 打印banner(spring图案和版本信息等)
            Banner printedBanner = printBanner(environment);
            // 创建应用程序上下文对象(默认为AnnotationConfigServletWebServerApplicationContext对象)
            GenericWebApplicationContext
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

    /**
     * SYSTEM_PROPERTY_JAVA_AWT_HEADLESS值为java.awt.headless
     * 服务器没有显示屏什么的,你得告诉程序一声,你工作的地方没有这些设备。
     * 如果需要,自己模拟,默认为true
     */
    private void configureHeadlessProperty() {
        System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
                System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        // 创建一个SpringApplicationRunListeners,参数为getSpringFactoriesInstances获取到的实例
        return new SpringApplicationRunListeners(logger,
                getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        // 获取加载器,底层逻辑是先获取resourceLoader的类加载器,
        // 没有的话然后用ClassUtils.getDefaultClassLoader();获取默认的类加载器
        ClassLoader classLoader = getClassLoader();
        // 根据类型获取spring.factories中符合的类名
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 根据以上获取的类名创建类的实例
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 将实例进行排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

    /**
     * 获取key值是factoryType的列表
     */
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        // 获取传入的class的name
        String factoryTypeName = factoryType.getName();
        // 从spring.factories中获取指定的类型,如果没有的话,返回一个空List
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 从缓存中根据类加载器获取MultiValueMap(value为类名list)
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        // 缓存中没有获取到的话
        // FACTORIES_RESOURCE_LOCATION META-INF/spring.factories
        // 使用指定类加载器获取所有包中的META-INF/spring.factories
        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 properties = PropertiesLoaderUtils.loadProperties(resource);
                // 遍历 Properties 中的键值对
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            // 将spring.factories中的键值对结果以类加载器为key存入缓存中,以便下次调用直接从缓存中读取
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

    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);
                // instanceClass必须为type的子类
                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;
    }

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                       ApplicationArguments applicationArguments) {
        // 创建或者获取一个ConfigurableEnvironment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //  配置 property sources 和 profiles
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 给指定的environment添加一个ConfigurationPropertySource的支持
        ConfigurationPropertySources.attach(environment);
        // 触发环境配置相关的监听事件
        listeners.environmentPrepared(environment);
        // 将配置文件中的spring.main开头的配置信息绑定到SpringApplication类对应的属性中
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            // 转换environment类型
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        // 给指定的environment添加一个ConfigurationPropertySource的支持
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

    /**
     * 根据webApplicationType类型创建不同的environment
     */
    private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
        }
    }

    class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {

        public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

        public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

        public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

        /**
         * 向propertySources中添加propertySources,目前是空的
         * Stub 存根,站位,差不多意思就是目前是空的,占个位置
         */
        @Override
        protected void customizePropertySources(MutablePropertySources propertySources) {
            propertySources.addLast(new PropertySource.StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
            propertySources.addLast(new PropertySource.StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
            if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
                propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
            }
            super.customizePropertySources(propertySources);
        }

        /**
         * 这是初始化servletProperties方法,此方法会在启动流程的刷新容器方法中被调用,会将上面的propertySource 的值进行填充
         */
        @Override
        public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
            WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
        }

    }

    class StandardEnvironment extends AbstractEnvironment {

        public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

        public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

        @Override
        protected void customizePropertySources(MutablePropertySources propertySources) {
            // 向propertySources 添加二个propertySource,一个是系统的属性,一个是系统的环境,直接就是有值的
            // getSystemProperties() 会获得系统的属性值
            // getSystemEnvironment() 会获得系统的环境值
            propertySources.addLast(
                    new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
            propertySources.addLast(
                    new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
        }
    }

    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            // 采用单例模式获取一个ConversionService 对象
            // ConversionService用于类型转化,我看了下加载完之后有136个转换器,例如String-》Number
            // 在此之前EventPublishingRunListener中发送的starting() 事件触发了一个BackgroundPreinitializer
            // 对此进行了预初始化
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            // 将类型转换对象设置到环境中去
            environment.setConversionService((ConfigurableConversionService) conversionService);
        }
        // 配置属性资源
        configurePropertySources(environment, args);
        // 配置哪个配置文件生效
        configureProfiles(environment, args);
    }

    protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        // 获取环境对象中的PropertySources,系统属性 系统环境 servletConfigInitParams servletContextInitParams
        MutablePropertySources sources = environment.getPropertySources();
        // 看看启动SpringApplicaiton的时候,有没有设置默认的属性以键值对的形式,有的话就加进去
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        // addCommandLineProperties默认为true,如果启动时传入了参数
        if (this.addCommandLineProperties && args.length > 0) {
            // name值为commandLineArgs
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            // 如果sources中包含name
            if (sources.contains(name)) {
                // 根据name获取到该source
                PropertySource<?> source = sources.get(name);
                // 以commandLineArgs 为key 创建一个CompositePropertySource(符合资源对象)对象
                CompositePropertySource composite = new CompositePropertySource(name);
                // 向composite里加入 一个以springApplicationCommandLineArgs为key,传入的参数为值的PropertySource
                composite.addPropertySource(
                        new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                // 并且将source 也加入到 composite中
                composite.addPropertySource(source);
                // 用composite替换原来对象
                // 将属性中的命令行参数和spring启动的参数都放进composite中,并用composite替换原来的值
                sources.replace(name, composite);
            }
            // 如果只有spring启动传入的参数的话,则以commandLineArgs为key,参数为值添加到最sources的最前面
            else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }

    protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
        Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
        // 从遍历PropertySources,从中获取属性为spring.profiles.active的属性
        profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        // 设置环境中生效的profiles,可以同时生效多个
        environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }

    /**
     * 给指定的环境添加一个ConfigurationPropertySource的支持
     * 将环境管理的每个PropertySource调整为ConfigurationPropertySource
     * 并允许经典的PropertySourcesPropertyResolver调用来解决使用
     * 所附的解析器将动态地跟踪底层环境属性源的任何添加或删除
     * 目前按我的理解这个绑定就是给所有的sources换了个类型,这个类型提供一些方法可以更好的追踪,删除加入等操作
     */
    public static void attach(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        // 获取environment中的PropertySources
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        // 获取sources中的configurationProperties
        PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
        // 如果sources 中含有 configurationProperties,并且其中的source不是最新的,则移除他
        if (attached != null && attached.getSource() != sources) {
            sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
            attached = null;
        }
        // 在sources的最前面添加ConfigurationPropertySourcesPropertySource类型的configurationProperties
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                    new SpringConfigurationPropertySources(sources)));
        }
    }

    /**
     * 调用environmentPrepared函数发送环境已经准备好的事件,从而触发监听器执行方法
     */
    void environmentPrepared(ConfigurableEnvironment environment) {
        for (org.springframework.boot.SpringApplicationRunListener listener : this.listeners) {
            // 该方法会间接调用如下方法multicastEvent
            listener.environmentPrepared(environment);
        }
    }
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        // getApplicationListeners(event, type)会获取到七个监听器
        // 七个监听器分别为:
        //0 = {ConfigFileApplicationListener@2420} 根据传入的参数,以及生效的profile 来加载配置文件
        //1 = {AnsiOutputApplicationListener@2421} 用于配置ansi编码
        //2 = {LoggingApplicationListener@2422} 根据配置的属性设置配置日志
        //3 = {ClasspathLoggingApplicationListener@2423} 根据日志的输出等级打印classpath的信息
        //4 = {BackgroundPreinitializer@2424} 在此事件中不会执行操作
        //5 = {DelegatingApplicationListener@2425} 在此事件中不会执行操作
        //6 = {FileEncodingApplicationListener@2426} 根据配置来判断文件的类型是否符合配置
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

    /**
     * 配置了一个spring.beaninfo.ignore属性,默认为TRUE,跳过对 BeanInfo 的搜索,
     * 这个BeanInfo是JDK 声明的一个接口,可以使用Introspector.getBeanInfo获取一个对象的方法、属性、事件和其他功能的显式信息。
     * 设置spring.beaninfo.ignore,将使用低级反射和应用标准设计模式自动分析某个类的信息,可以提升一些性能。
     */
    private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
        if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
            Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
            System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
        }
    }



    /**
     * 该接口对应springApplicaiton.run的各个阶段
     * EventPublishingRunListener实现了下面接口
     */
    interface SpringApplicationRunListener {
        /**
         * run方法启动时立即调用,可以用于非常早期的初始化
         */
        default void starting() {
        }
        /**
         * 在环境准备好,但在ApplicaitonContext创建之前执行
         */
        default void environmentPrepared(ConfigurableEnvironment environment) {
        }
        /**
         * 在ApplicationContext创建和准备好,但在资源加载前
         */
        default void contextPrepared(ConfigurableApplicationContext context) {
        }
        /**
         * application context 加载完,但在刷新之前
         */
        default void contextLoaded(ConfigurableApplicationContext context) {
        }
        /**
         * context 刷新完并且 application 已完成,但是在 CommandLineRunner 和 ApplicationRunner 被调用前
         */
        default void started(ConfigurableApplicationContext context) {
        }
        /**
         * 在run方法运行结束前调用,当application context 已经刷新 并且 CommandLineRunner 和 ApplicationRunner也已经
         * 被调用
         */
        default void running(ConfigurableApplicationContext context) {
        }
        /**
         * 在运行程序的时候发生错误的时候调用
         */
        default void failed(ConfigurableApplicationContext context, Throwable exception) {
        }

    }