在上一篇中,我們的類加載器使用environment獲取一些屬性,如下圖
下面我們介紹下environment的使用
1、進入啟動方法run,定位到prepareEnvironment方法
2、進到prepareEnvironment方法
3、進入getOrCreateEnvironment方法。實例化了StandardServletEnvironment
進入父類AbstractEnvironment的構造函數
1)、進入customizePropertySources方法。
增加servletConfigInitParams屬性源和servletContextInitParams屬性源,添加jndi屬性源
然后調用父類的super.customizePropertySources(propertySources);
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); }
2)、進入customizePropertySources,增加兩個屬性源(系統屬性源和系統環境屬性源)
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())); }
里面getSystemProperties方法,主要有Java的版本號,Java虛擬機名稱等
4、然后回到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); }
1) 進入configurePropertySources(environment, args);
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)); } } }
這個方法主要是添加defaultProperties默認屬性源(sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
)和命令屬性源sources.addFirst(new SimpleCommandLinePropertySource(args));
進入SimpleCommandLinePropertySource的構造函數
public SimpleCommandLinePropertySource(String... args) { super(new SimpleCommandLineArgsParser().parse(args)); }
里面調用了parse函數。命令的定義是我們前面介紹過的。解析命令的時候,判斷是否以“--”開頭
2) 進入configureProfiles。profile我們后面在進行介紹 SpringBoot Profile源碼介紹
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)); }
3、 listeners.environmentPrepared(environment)
發布environmentPrepared事件,這里就是監聽器的實現。進入environmentPrepared方法
public void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } }
進入listener.environmentPrepared(environment);里面調用廣播器廣播一個事件
@Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); }
進入廣播事件方法。
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
在此方法中,獲得對該事件感興趣的監聽器,遍歷監聽器,調用invokeListener方法。
監聽器的部分在前面已經介紹過了,這里我們主要看下監聽器都做了什么?
進入doInvokeListener方法
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
然后進入listener.onApplicationEvent(event);方法
然后進入onApplicationEnvironmentPreparedEvent方法
方法loadPostProcessors是獲得屬性文件中對該監聽器的實現類有哪些。
List<EnvironmentPostProcessor> loadPostProcessors() { return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader()); }
該配置文件配置如下:
配置文件由三個EnvironmentPostProcessor,在把當前的添加進去postProcessors.add(this);就有四個了。然后依次遍歷這四個EnvironmentPostProcessor
我們進入其中一個EnvironmentPostProcessor,如ConfigFileApplicationListener。里面調用了addPropertySources(environment, application.getResourceLoader());
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { addPropertySources(environment, application.getResourceLoader()); }
然后進入addPropertySources方法
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { RandomValuePropertySource.addToEnvironment(environment); new Loader(environment, resourceLoader).load(); }
RandomValuePropertySource.addToEnvironment(environment);這個方法添加了一個隨機的屬性源
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"); }
然后進入new Loader(environment, resourceLoader).load();的load方法。添加application-profile.(properties|yml)屬性集
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); } resetEnvironmentProfiles(this.processedProfiles); load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); addLoadedPropertySources(); }
關於profile留在后面介紹。 SpringBoot Profile源碼介紹
4、bindToSpringApplication(environment);
進入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); } }
5、environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
使用判斷convertEnvironmentIfNecessary當前環境是否是指定類型,是的話就返回。否的話再new一個環境,把值賦值到新的實例化的環境中
StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment, Class<? extends StandardEnvironment> type) { if (type.equals(environment.getClass())) { return (StandardEnvironment) environment; } return convertEnvironment(environment, type); } private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment, Class<? extends StandardEnvironment> type) { StandardEnvironment result = createEnvironment(type); result.setActiveProfiles(environment.getActiveProfiles()); result.setConversionService(environment.getConversionService()); copyPropertySources(environment, result); return result; }
6、ConfigurationPropertySources.attach(environment); 增加configurationProperties屬性源