框架: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);
			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開始分析,現在感覺思路亂沒關係,最後我會從頭到尾再理一遍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章