javascript
SpringBoot基础-Environment解析
獲取屬性源碼跟蹤解析
入口方法
@Component public class EnvironmentDebugger implements CommandLineRunner, EnvironmentAware {private Environment environment;@Overridepublic void run(String... args) throws Exception {String study2 = environment.getProperty("study2");}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;} }實(shí)現(xiàn)EnvironmentAware 接口,獲取Environment對象
實(shí)現(xiàn)CommandLineRunner接口,在容器啟動(dòng)完成后,獲取key的值,作為源碼跟蹤的入口
源碼跟蹤
根據(jù)入口environment.getProperty,一直點(diǎn)進(jìn)入org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(java.lang.String, java.lang.Class, boolean),可以看到最基礎(chǔ)的實(shí)現(xiàn)
@Nullable protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {for (PropertySource<?> propertySource : this.propertySources) {if (logger.isTraceEnabled()) {logger.trace("Searching for key '" + key + "' in PropertySource '" +propertySource.getName() + "'");}Object value = propertySource.getProperty(key);if (value != null) {if (resolveNestedPlaceholders && value instanceof String) {value = resolveNestedPlaceholders((String) value);}logKeyFound(key, propertySource, value);return convertValueIfNecessary(value, targetValueType);}}}if (logger.isTraceEnabled()) {logger.trace("Could not find key '" + key + "' in any property source");}return null; }方法中可以看出通過key獲取屬性值,遍歷propertySources中存放的PropertySource,并且從中依次取出屬性,如果所有都沒有取到,則返回NULL
可以看出propertySources中共存在7中類型的屬性,可以找到我們設(shè)置的默認(rèn)屬性,并且已鍵值對存放數(shù)據(jù)
PropertySource源碼
public abstract class PropertySource<T> {protected final Log logger = LogFactory.getLog(getClass());protected final String name;protected final T source; }source為存放屬性的具體實(shí)現(xiàn)
通過獲取屬性可以看出所有的屬性都存放在:propertySources中,只需要搞懂spring容器在啟動(dòng)的時(shí)候是如何裝載自定義的屬性進(jìn)propertySources就可以
Environment加載屬性
入口函數(shù)
springApplication.run(args) --> prepareEnvironment
prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);listeners.environmentPrepared(bootstrapContext, environment);DefaultPropertiesPropertySource.moveToEnd(environment);configureAdditionalProfiles(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment; }在prepareEnvironment中,共進(jìn)行了一下的進(jìn)步操作:
getOrCreateEnvironment
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();} }- 通過:webApplicationType判斷當(dāng)前容器環(huán)境,創(chuàng)建不同的Environment對象
SERVLET環(huán)境舉例
public class StandardEnvironment extends AbstractEnvironment {/** System environment property source name: {@value}. */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value}. */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}@Overridepublic void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);} }可以看出在類種定義了方法:customizePropertySources,
customizePropertySources:
- 添加:servletContextInitParams屬性集
- 添加:servletConfigInitParams屬性集
- 如果jndi環(huán)境,添加jndiProperties屬性集
- 調(diào)用付類customizePropertySources方法,初始化java及系統(tǒng)屬性參數(shù)
supper.customizePropertySources()
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.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); }
在StandardServletEnvironment類中,并沒有發(fā)現(xiàn)在哪里調(diào)用:customizePropertySources().此時(shí)查看類圖,它的的上層父類:StandardEnvironment也沒有調(diào)用,繼續(xù)查看上層父類:AbstractEnvironment,此時(shí)發(fā)現(xiàn)在構(gòu)造函數(shù)中調(diào)用了customizePropertySources()
只是由于java在實(shí)例化的時(shí)候,會(huì)先調(diào)用父類的構(gòu)造函數(shù),所在執(zhí)行了customizePropertySources(),完成相關(guān)屬性的初始化工作
- 此時(shí)environment中的屬性只有四種
servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment
configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {// 添加解析器if (this.addConversionService) {ConversionService conversionService = ApplicationConversionService.getSharedInstance();environment.setConversionService((ConfigurableConversionService) conversionService);}configurePropertySources(environment, args);configureProfiles(environment, args); }在方法中通過:configurePropertySources方法來添加:
- 添加默認(rèn)屬性值
- 添加命令行屬性值
configurePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {MutablePropertySources sources = environment.getPropertySources();DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);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));}} }1.通過函數(shù):DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast) 將硬編碼的defaultProperties對象屬性添加在sources中
public static void main(String[] args) throws InterruptedException {SpringApplication springApplication = new SpringApplication((SpringBootDemoApplication.class));Properties properties = new Properties();properties.setProperty("study1", "value_study1");springApplication.setDefaultProperties(properties);springApplication.run(args); }//springApplication.setDefaultProperties(properties) public void setDefaultProperties(Properties defaultProperties) {this.defaultProperties = new HashMap<>();for (Object key : Collections.list(defaultProperties.propertyNames())) {this.defaultProperties.put((String) key, defaultProperties.get(key));} }2.判斷是否需要添加命令行參數(shù)及args是否傳入的信息,構(gòu)建一個(gè)SimpleCommandLinePropertySource對象添加到屬性列表中
new SimpleCommandLinePropertySource(args)
調(diào)用SimpleCommandLineArgsParser解析器,解析傳入的參數(shù)信息
SimpleCommandLineArgsParser.parse
class SimpleCommandLineArgsParser {/*** Parse the given {@code String} array based on the rules described {@linkplain* SimpleCommandLineArgsParser above}, returning a fully-populated* {@link CommandLineArgs} object.* @param args command line arguments, typically from a {@code main()} method*/public CommandLineArgs parse(String... args) {CommandLineArgs commandLineArgs = new CommandLineArgs();for (String arg : args) {if (arg.startsWith("--")) {String optionText = arg.substring(2);String optionName;String optionValue = null;int indexOfEqualsSign = optionText.indexOf('=');if (indexOfEqualsSign > -1) {optionName = optionText.substring(0, indexOfEqualsSign);optionValue = optionText.substring(indexOfEqualsSign + 1);}else {optionName = optionText;}if (optionName.isEmpty()) {throw new IllegalArgumentException("Invalid argument syntax: " + arg);}commandLineArgs.addOptionArg(optionName, optionValue);}else {commandLineArgs.addNonOptionArg(arg);}}return commandLineArgs;}}可以看出只對-- 開頭的字符串進(jìn)行解析,根據(jù)鍵值對來獲取key,value,添加到屬性中
- 此時(shí)environment中的屬性值
servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment、commandLineArgs、defaultProperties
ConfigurationPropertySources.attach
public static void attach(Environment environment) {Assert.isInstanceOf(ConfigurableEnvironment.class, environment);MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);if (attached != null && attached.getSource() != sources) {sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);attached = null;}if (attached == null) {sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,new SpringConfigurationPropertySources(sources)));} }主要設(shè)置configurationProperties屬性值
- 此時(shí)environment屬性值
servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment、commandLineArgs、defaultProperties、 configurationProperties
listeners.environmentPrepared
DefaultPropertiesPropertySource.moveToEnd
public static final String NAME = "defaultProperties";public static void moveToEnd(MutablePropertySources propertySources) {PropertySource<?> propertySource = propertySources.remove(NAME);if (propertySource != null) {propertySources.addLast(propertySource);} }先將defaultProperties刪除,然后在添加,目的是將defaultProperties存到最后一個(gè)節(jié)點(diǎn),用作兜底操作
configureAdditionalProfiles
private void configureAdditionalProfiles(ConfigurableEnvironment environment) {if (!CollectionUtils.isEmpty(this.additionalProfiles)) {Set<String> profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles()));if (!profiles.containsAll(this.additionalProfiles)) {profiles.addAll(this.additionalProfiles);environment.setActiveProfiles(StringUtils.toStringArray(profiles));}} }獲取當(dāng)前需要加載的配置文件后綴
bindToSpringApplication
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);} }將spring.main屬性值賦值給:SpringApplication中對應(yīng)的屬性
ConfigurationClassParser
- 添加@PropertySources屬性集,在run的refresh中調(diào)用
org.springframework.context.annotation.ConfigurationClassParser#processPropertySource
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {String name = propertySource.getString("name");if (!StringUtils.hasLength(name)) {name = null;}String encoding = propertySource.getString("encoding");if (!StringUtils.hasLength(encoding)) {encoding = null;}String[] locations = propertySource.getStringArray("value");Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));for (String location : locations) {try {String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);Resource resource = this.resourceLoader.getResource(resolvedLocation);addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));}catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {// Placeholders not resolvable or resource not found when trying to open itif (ignoreResourceNotFound) {if (logger.isInfoEnabled()) {logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());}}else {throw ex;}}} }private void addPropertySource(PropertySource<?> propertySource) {String name = propertySource.getName();MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();if (this.propertySourceNames.contains(name)) {// We've already added a version, we need to extend itPropertySource<?> existing = propertySources.get(name);if (existing != null) {PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?((ResourcePropertySource) propertySource).withResourceName() : propertySource);if (existing instanceof CompositePropertySource) {((CompositePropertySource) existing).addFirstPropertySource(newSource);}else {if (existing instanceof ResourcePropertySource) {existing = ((ResourcePropertySource) existing).withResourceName();}CompositePropertySource composite = new CompositePropertySource(name);composite.addPropertySource(newSource);composite.addPropertySource(existing);propertySources.replace(name, composite);}return;}}if (this.propertySourceNames.isEmpty()) {propertySources.addLast(propertySource);}else {String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);propertySources.addBefore(firstProcessed, propertySource);}this.propertySourceNames.add(name); }總結(jié)
以上是生活随笔為你收集整理的SpringBoot基础-Environment解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WordPiece、BPE详解及代码
- 下一篇: 如何在 arm 官网上找到合适的手册