• SpringApplication到底run了什么(上)


    在上篇文章:SpringBoot源码解析:创建SpringApplication对象实例中,我们详细描述了SpringApplication对象实例的创建过程,本篇文章继续看run方法的执行逻辑吧

    	public ConfigurableApplicationContext run(String... args) {
    		StopWatch stopWatch = new StopWatch();
    		stopWatch.start();
    		ConfigurableApplicationContext context = null;
    		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    		configureHeadlessProperty();
    		SpringApplicationRunListeners listeners = getRunListeners(args);
    		listeners.starting();
    		try {
    			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
    					args);
    			ConfigurableEnvironment environment = prepareEnvironment(listeners,
    					applicationArguments);
    			//后面还有,本篇文章就解析到这。。。。
    	}
    
    1. 第一行使用了StopWatch来记录开始时间
    2. 设置了java.awt.headless环境变量,在网上了解了一下这个变量的相关信息

    Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式

    个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正

    1. 接着遍历所有构造SpringApplication实例时加载的SpringApplicationRunListener,调用它们的started方法

    这里构造时仅仅加载了一个EventPublishingRunListener类,所以咱们就来解析一下这个东东

        public void starting() {
            this.initialMulticaster.multicastEvent(
                    new ApplicationStartingEvent(this.application, this.args));
        }
    

    可以看到这里调用了SimpleApplicationEventMulticaster类的multicastEvent方法并且传入了ApplicationStartingEvent对象,看名字就知道了这个是SpringBoot启动事件

    
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(() -> invokeListener(listener, event));
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }
    

    其中获取监听器使用的是getApplicationListeners方法,这个方法中主要就是从最启动时获取的所有监听器和这个事件做了下匹配,返回通过匹配的监听器集合

    接着就是看是否设置线程池参数,如果有线程池则使用线程池的线程进行操作,否则将同步调用监听器

    1. 把所有的命令行启动参数封装成ConfigurableEnvironment对象
    2. 准备运行时环境
    	private ConfigurableEnvironment prepareEnvironment(
    			SpringApplicationRunListeners listeners,
    			ApplicationArguments applicationArguments) {
    		ConfigurableEnvironment environment = getOrCreateEnvironment();
    		configureEnvironment(environment, applicationArguments.getSourceArgs());
    		listeners.environmentPrepared(environment);
    		if (!this.webEnvironment) {
    			environment = new EnvironmentConverter(getClassLoader())
    					.convertToStandardEnvironmentIfNecessary(environment);
    		}
    		return environment;
    	}
    
    获取或创建环境getOrCreateEnvironment

    方法名就很直观,有就直接获取,没有就新建

    	private ConfigurableEnvironment getOrCreateEnvironment() {
    		if (this.environment != null) {
    			return this.environment;
    		}
    		if (this.webApplicationType == WebApplicationType.SERVLET) {
    			return new StandardServletEnvironment();
    		}
    		return new StandardEnvironment();
    	}
    

    上篇文章中说过了,咱们是Servlet环境,所以当前方法是返回一个StandardServletEnvironment对象,这个对象的构造过程中调用了customizePropertySources方法(它父类的父类调用的)

    	protected void customizePropertySources(MutablePropertySources propertySources) {
    		propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    		propertySources.addLast(new StubPropertySource("servletContextInitParams"));
    		if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
    			propertySources.addLast(new JndiPropertySource("jndiProperties"));
    		}
    		super.customizePropertySources(propertySources);
    	}
      //这是它父类的
     protected void customizePropertySources(MutablePropertySources propertySources) {
    		propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties()));
    		propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
    	}
    

    可以看出StandardServletEnvironmentpropertySources中添加了两个StubPropertySource对象,而它的父类添加了一个包含java系统属性和一个操作系统环境变量的对象

    配置 configureEnvironment
    
    protected void configureEnvironment(ConfigurableEnvironment environment,
        String[] args) {
        // 配置PropertySources
        configurePropertySources(environment, args);
        // 配置Profiles
        configureProfiles(environment, args);
    }
    

    分别看一下两个方法

    配置PropertySources
    protected void configurePropertySources(ConfigurableEnvironment environment,
            String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            // 存在默认配置将其放到最后位置
            sources.addLast(
                    new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        // 如果存在命令行参数则将原有的替换掉
        if (this.addCommandLineProperties && args.length > 0) {
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(new SimpleCommandLinePropertySource(
                        "springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            }
            else {
                // 将其放到第一位置
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }
    

    这里就体现出了这个命令行参数比应用配置文件的优先级高的情况了

    配置Profiles

    从PropertySources中查找spring.profiles.active属性,存在则将其值添加activeProfiles集合中

    protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
        environment.getActiveProfiles(); 
        Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
        profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }
    
    发布EnvirongmentPreparedEvent事件
    绑定环境
    protected void bindToSpringApplication(ConfigurableEnvironment environment) {
    		try {
    			Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
    		}
    		catch (Exception ex) {
    			throw new IllegalStateException("Cannot bind to SpringApplication", ex);
    		}
    	}
    
    转换环境

    如果web环境变更为NONE则将StandardServletEnvironment转换为StandardEnvironment

    ConfigurationPropertySources.attach(environment)
    public static void attach(Environment environment) {
    		Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
    		MutablePropertySources sources = ((ConfigurableEnvironment) environment)
    				.getPropertySources();
    		PropertySource<?> attached = sources.get("configurationProperties");
    		if (attached != null && attached.getSource() != sources) {
    			sources.remove("configurationProperties");
    			attached = null;
    		}
    		if (attached == null) {
    			sources.addFirst(new ConfigurationPropertySourcesPropertySource(
    					"configurationProperties",
    					new SpringConfigurationPropertySources(sources)));
    		}
    	}
    

    最终这个sources对象的第一个位置放的是它自己,循环引用,这个具体的含义还有待挖掘

    1

  • 相关阅读:
    go开发常用工具
    go 组合取代继承
    go 的struct 跟map赋值不同
    Go进阶35:Go语言自定义自己的SSHServer harder
    Go进阶18:常用加密解密算法总结
    golang处理时区
    Go的定时器cron
    Docker Alpine 容器修改/etc/hosts 不生效的问题
    成为 Go 高手的 8 个 GitHub 开源项目
    C#知识点总结系列:3、C#中Delegate和Event以及它们的区别
  • 原文地址:https://www.cnblogs.com/zhixiang-org-cn/p/11575705.html
Copyright © 2020-2023  润新知