SpringBoot之SpringApplication(2)

我们回到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);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			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;
	}

上一篇介绍了SpringApplication的构造跟run方法介绍到执行ApplicationStartingEvent事件对应的listeners的相应方法执行。

我们继续往下看。将传入的参数构造成DefaultApplicationArguments实例

	public DefaultApplicationArguments(String[] args) {
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args);
		this.args = args;
	}
可以看到这里根据启动时传入的参数构造了source实例
		Source(String[] args) {
			super(args);
		}
source仅仅调用了父类的构造
    public SimpleCommandLinePropertySource(String... args) {
        super((new SimpleCommandLineArgsParser()).parse(args));
    }
这里实例化了SimpleCommandLineArgsParser的实例,并调用其parse方法解析启动时的命令行参数,返回CommandLine继续传递给父类的构造。
    SimpleCommandLineArgsParser() {
    }

    public CommandLineArgs parse(String... args) {
        CommandLineArgs commandLineArgs = new CommandLineArgs();
        String[] var3 = args;
        int var4 = args.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String arg = var3[var5];
            if(arg.startsWith("--")) {
                String optionText = arg.substring(2, arg.length());
                String optionValue = null;
                String optionName;
                if(optionText.contains("=")) {
                    optionName = optionText.substring(0, optionText.indexOf(61));
                    optionValue = optionText.substring(optionText.indexOf(61) + 1, optionText.length());
                } else {
                    optionName = optionText;
                }

                if(optionName.isEmpty() || optionValue != null && optionValue.isEmpty()) {
                    throw new IllegalArgumentException("Invalid argument syntax: " + arg);
                }

                commandLineArgs.addOptionArg(optionName, optionValue);
            } else {
                commandLineArgs.addNonOptionArg(arg);
            }
        }

        return commandLineArgs;
    }

首先初始化一个CommandLineArgs,然后遍历args,如果args以“--”开头,于是以“=”分割加入到OptionArg中,否则直接加入到NonOptionArg中。

我们一直看其继承链上的的构造函数

    public CommandLinePropertySource(T source) {
        super("commandLineArgs", source);
    }

    public EnumerablePropertySource(String name, T source) {
        super(name, source);
    }

    public PropertySource(String name, T source) {
        this.logger = LogFactory.getLog(this.getClass());
        Assert.hasText(name, "Property source name must contain at least one character");
        Assert.notNull(source, "Property source must not be null");
        this.name = name;
        this.source = source;
    }
到这里,DefaultApplicationArguments实例构造完成。继续回到SpringApplication的run方法,调用prepareEnvironment方法并传入listeners跟刚刚构造的applicationArguments
	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
调用getOrCreateEnviroment方法获取或者创建ConfigurableEnvironment
	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		if (this.webApplicationType == WebApplicationType.SERVLET) {
			return new StandardServletEnvironment();
		}
		return new StandardEnvironment();
	}

先判断environment是否为空,不为空则直接返回,如果这里是web环境那么返回新构造的StandardServletEnvironment,否则返回新构造的StandardEnvironment。以一般情况StandardEnvironment介绍,其构造方法为空,具体构造的逻辑在其父类AbstractEnvironment中

    public AbstractEnvironment() {
        this.propertySources = new MutablePropertySources(this.logger);
        this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
        this.customizePropertySources(this.propertySources);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Initialized " + this.getClass().getSimpleName() + " with PropertySources " + this.propertySources);
        }

    }
这里构造了Source、Resolver实例,调用customizePropertySources方法并传入之前构造的Source(propertySource)。这个方法在这里是空的。子类StandardEnvironment覆盖了其父类方法,做了具体实现。
    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new MapPropertySource("systemProperties", this.getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
    }
将系统数据与环境添加进来。

得到ConfigurableEnvironment后,然后调用configureEnvironment方法配置环境。

	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}
先看configurePropertySources方法
	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));
			}
		}
	}

先得到之前生成的Sources,如果此时defaultProperties不为空,那么把defaultProperties添加到Sources尾部,如果addCommandLineProperties为true,并且命令参数个数不为0,那么先得到CommandLineArgs字符串,如果该字符串存在于sources的链中,那么将参数构造成composite,然后替换掉字符串在sources的链中的位置。否则直接构造SimpleCommandLinePropertySource添加到链的头部。

此时sources中存在commandLineArgs、systemProperties、systemEnvironment、defaultProperties等

我们继续看其Profiles的配置,调用configureProfiles方法

	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		environment.getActiveProfiles(); // ensure they are initialized
		// But these ones should go first (last wins in a property key clash)
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}
可以看到,先获得activeProfiles配置,将环境中的activeProfiles存入到有序的linkedSet中,变成数组,存入回环境中的activeProfiles。environment的具体逻辑在AbstractEnvironment中。
    protected Set<String> doGetActiveProfiles() {
        Set var1 = this.activeProfiles;
        synchronized(this.activeProfiles) {
            if (this.activeProfiles.isEmpty()) {
                String profiles = this.getProperty("spring.profiles.active");
                if (StringUtils.hasText(profiles)) {
                    this.setActiveProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));
                }
            }

            return this.activeProfiles;
        }
    }

由于spring boot外置配置属性优先级高于代码级。这里先得到activeProfiles看其是否为空,如果不为空那么直接返回,为空再去获得"spring.profiles.active"对应的属性。

配置完成后调用listeners的environmentPrepared方法并传入environment,通知所有的对其感兴趣的listener。调用的是SpringApplicationRunListeners的environmentPrepared方法,内部调用EventPublishingRunListener的environmentPrepared 并且调用的事件类型为ApplicationEnvironmentPreparedEvent.

查配置文件得到感兴趣的类

org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
分别调用了他们的onApplicationEvent方法,先来看ConfigFileApplicationListener的
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent(
					(ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}
这里调用了onApplicationEnvironmentPreparedEvent方法
	private void onApplicationEnvironmentPreparedEvent(
			ApplicationEnvironmentPreparedEvent event) {
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(),
					event.getSpringApplication());
		}
	}
先调用loadPostProcessors()方法,得到配置的EveironmentPostProcessor集合
	List<EnvironmentPostProcessor> loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
				getClass().getClassLoader());
	}
其实这个形式的方法我们已经很熟悉了,调用SpringFactoriesLoader的loadFactories方法,得到配置文件(\META-INF\spring.factories)中指定的EnvironmentPostProcessor类为key的集合的类的实例。
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

回到onApplicationEnvironmentPreparedEvent方法,得到postProcessors后,把自己即ConfigFileApplicationListener实例也加进去,然后排序,分别调用其postProcessEnvironment方法。

先看CloudFoundryVcapEnvironmentPostProcessor的postProcessEnvironment方法

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
		if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
			Properties properties = new Properties();
			JsonParser jsonParser = JsonParserFactory.getJsonParser();
			addWithPrefix(properties,
					getPropertiesFromApplication(environment, jsonParser),
					"vcap.application.");
			addWithPrefix(properties, getPropertiesFromServices(environment, jsonParser),
					"vcap.services.");
			MutablePropertySources propertySources = environment.getPropertySources();
			if (propertySources.contains(
					CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
				propertySources.addAfter(
						CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
						new PropertiesPropertySource("vcap", properties));
			}
			else {
				propertySources
						.addFirst(new PropertiesPropertySource("vcap", properties));
			}
		}
	}
先判断环境是否是在 CloudFoundry中,如果不在则不处理,直接返回。
	CLOUD_FOUNDRY {

		@Override
		public boolean isActive(Environment environment) {
			return environment.containsProperty("VCAP_APPLICATION")
					|| environment.containsProperty("VCAP_SERVICES");
		}

	},

如果在,那么继续。将“vacp.application.”,"vamp.services."开头的配置信息添加到propertis中,再判断environment中的propertySources中是否有名为“commandLineArgs”的source,有的话则把properties以名为“vcap”的形式存入到“commandLineArgs”资源后面。如果没有则直接放入队首。

继续看SpringApplicationJsonEnvironmentPostProcessor的postProcessEnvironment方法

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
		MutablePropertySources propertySources = environment.getPropertySources();
		propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull)
				.findFirst().ifPresent((v) -> processJson(environment, v));
	}
获取propertySources,再遍历其中,得到有JsonPropertyValue的配置,如果有则对每个JsonPropertyValue调用processJson方法处理。
	private void processJson(ConfigurableEnvironment environment,
			JsonPropertyValue propertyValue) {
		try {
			JsonParser parser = JsonParserFactory.getJsonParser();
			Map<String, Object> map = parser.parseMap(propertyValue.getJson());
			if (!map.isEmpty()) {
				addJsonPropertySource(environment,
						new JsonPropertySource(propertyValue, flatten(map)));
			}
		}
		catch (Exception ex) {
			logger.warn("Cannot parse JSON for spring.application.json: "
					+ propertyValue.getJson(), ex);
		}
	}

解析json为map,再添加到environment中。

接下来是SystemEnvironmentPropertySourceEnvironmentPostProcessor的

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
		String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
		PropertySource<?> propertySource = environment.getPropertySources()
				.get(sourceName);
		if (propertySource != null) {
			replacePropertySource(environment, sourceName, propertySource);
		}
	}

	@SuppressWarnings("unchecked")
	private void replacePropertySource(ConfigurableEnvironment environment,
			String sourceName, PropertySource<?> propertySource) {
		Map<String, Object> originalSource = (Map<String, Object>) propertySource
				.getSource();
		SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(
				sourceName, originalSource);
		environment.getPropertySources().replace(sourceName, source);
	}

对systemenvironment资源调用replacePropertySource方法,将该资源封装成OriginAwareSystemEnvironmentPropertySource放入原资源位置。

接下来我们来看下ConfigFileApplicationListener的postProcessEnvironment方法

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
		addPropertySources(environment, application.getResourceLoader());
	}
	protected void addPropertySources(ConfigurableEnvironment environment,
			ResourceLoader resourceLoader) {
		RandomValuePropertySource.addToEnvironment(environment);
		new Loader(environment, resourceLoader).load();
	}
先调用了RandomValuePropertySource的addToEnbvironment方法
	public static void addToEnvironment(ConfigurableEnvironment environment) {
		environment.getPropertySources().addAfter(
				StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
				new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
		logger.trace("RandomValuePropertySource add to Environment");
	}

在systemEnvironment资源的后面添加了名为random的RandomValuePropertySource资源。可以关注下其取数据的函数例如${random.int}

	@Override
	public Object getProperty(String name) {
		if (!name.startsWith(PREFIX)) {
			return null;
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Generating random property for '" + name + "'");
		}
		return getRandomValue(name.substring(PREFIX.length()));
	}

	private Object getRandomValue(String type) {
		if (type.equals("int")) {
			return getSource().nextInt();
		}
		if (type.equals("long")) {
			return getSource().nextLong();
		}
		String range = getRange(type, "int");
		if (range != null) {
			return getNextIntInRange(range);
		}
		range = getRange(type, "long");
		if (range != null) {
			return getNextLongInRange(range);
		}
		if (type.equals("uuid")) {
			return UUID.randomUUID().toString();
		}
		return getRandomBytes();
	}
可以看到其结果,random.nextInt()。

接下来构造了Loader实例,并调用其load方法加载资源。

		Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
			this.environment = environment;
			this.resourceLoader = (resourceLoader != null ? resourceLoader
					: new DefaultResourceLoader());
			this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
					PropertySourceLoader.class, getClass().getClassLoader());
		}

		public void load() {
			this.profiles = new LinkedList<>();
			this.processedProfiles = new LinkedList<>();
			this.activatedProfiles = false;
			this.loaded = new LinkedHashMap<>();
			initializeProfiles();
			while (!this.profiles.isEmpty()) {
				Profile profile = this.profiles.poll();
				if (profile != null && !profile.isDefaultProfile()) {
					addProfileToEnvironment(profile.getName());
				}
				load(profile, this::getPositiveProfileFilter,
						addToLoaded(MutablePropertySources::addLast, false));
				this.processedProfiles.add(profile);
			}
			load(null, this::getNegativeProfileFilter,
					addToLoaded(MutablePropertySources::addFirst, true));
			addLoadedPropertySources();
		}
构造可以看到无非是一些赋值,其中需要注意的是propertySourceLoaders无非是
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

关注其load方法,调用了initializeProfiles方法初始化配置文件。

		private void initializeProfiles() {
			// The default profile for these purposes is represented as null. We add it
			// first so that it is processed first and has lowest priority.
			this.profiles.add(null);
			Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
			this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
			// Any pre-existing active profiles set via property sources (e.g.
			// System properties) take precedence over those added in config files.
			addActiveProfiles(activatedViaProperty);
			if (this.profiles.size() == 1) { // only has null profile
				for (String defaultProfileName : this.environment.getDefaultProfiles()) {
					Profile defaultProfile = new Profile(defaultProfileName, true);
					this.profiles.add(defaultProfile);
				}
			}
		}
一开始profiles是空的linkedList实例,这里先调用getProfilesActivatedViaProperty方法得到activeProfiles
		private Set<Profile> getProfilesActivatedViaProperty() {
			if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)
					&& !this.environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) {
				return Collections.emptySet();
			}
			Binder binder = Binder.get(this.environment);
			Set<Profile> activeProfiles = new LinkedHashSet<>();
			activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
			activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY));
			return activeProfiles;
		}
一开始,如果environment中没有包含spring.profiles.active和spring.profiles.include的配置资源的话,直接返回空集合。如果含有配置,那么通过传入environment构造Binder,再通过binder得到spring.profiles.active和spring.profiles.include

有点长了,下次从load开始分析,现在感觉思路乱没关系,最后我会从头到尾再理一遍。


















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值