Reading guide
- The last article tells you how to get the running listener and what to do by calling the starting method.
- This article focuses on the process of preparing the environment in the process of springApplication.run
1. Detailed source code
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * Run a spring program, create and refresh a new ApplicationContext * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { // Create a timer StopWatch stopWatch = new StopWatch(); // Start timing stopWatch.start(); // Create a configurable ApplicationContext variable that can be configured ConfigurableApplicationContext context = null; // Create an exception report collection Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // Configure the Headless attribute (this will not be described in detail. It roughly means to turn on a Headless mode, which will deal with programs without input and output devices such as monitors) configureHeadlessProperty(); // Get running listener SpringApplicationRunListeners listeners = getRunListeners(args); // Call the listener's start method, and the listener's starting method will be called internally in batches to send events to indirectly call ApplicationListener listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 1.1 preparation environment 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; }
- Enter the topic, prepare environment (listeners, application arguments);
// 1.2 preparation environment // Parameter 1: listeners: there is a previously obtained eventPublishRunListener inside // Parameter 2: applicationArguments starts the passed in parameters ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
- Enter the prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Get or create an environment (SERVLET, REACTIVE, NONE according to the type of application) ConfigurableEnvironment environment = getOrCreateEnvironment(); // Configuration environment configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
- Go inside getOrCreateEnvironment()
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } // Create different environments according to the program type switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); } }
- Enter new StandardServletEnvironment()
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment { @Override protected void customizePropertySources(MutablePropertySources propertySources) { // SERVLET_CONFIG_PROPERTY_SOURCE_NAME servletConfigInitParams // SERVLET_CONTEXT_PROPERTY_SOURCE_NAME servletContextInitParams // Add two propertySources to the StandardServletEnvironment object, which is currently empty // Stub stub, station, almost means that it is currently empty and occupies a position propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } super.customizePropertySources(propertySources); } // This is to initialize the servletProperties method. This method will be called in the refresh container method of starting the process, and the values of the above two propertysources will be populated @Override public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); } }
- Jump to the parent class of StandardServletEnvironment, inside StandardEnvironment
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"; @Override protected void customizePropertySources(MutablePropertySources propertySources) { // Add two propertySources to propertySources. One is the system property and the other is the system environment, which is directly valuable // getSystemProperties() will get the property value of the system // getSystemEnvironment() will get the environment value of the system propertySources.addLast( new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast( new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); } }
- OK, so far, the new ConfigurableEnvironment object is created, and then other parameters in the configuration
- Jump to the inside of configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { // Obtain a ConversionService object in singleton mode // ConversionService is used for type conversion. I saw that there are 136 converters after loading, such as String - Number // Before that, the starting() event sent in eventpublishingrunnlistener triggered a BackgroundPreinitializer // This is pre initialized ConversionService conversionService = ApplicationConversionService.getSharedInstance(); // Set the type conversion object to the environment environment.setConversionService((ConfigurableConversionService) conversionService); } // Configure attribute resources configurePropertySources(environment, args); // Configure which profile takes effect configureProfiles(environment, args); }
- Jump to configurePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { // Get the PropertySources in the environment object. After reading this, I won't forget. There should be four, right // System properties system environment servletConfigInitParams servletContextInitParams. The first two are available and the second is empty MutablePropertySources sources = environment.getPropertySources(); // Check whether the default properties are set in the form of key value pairs when starting spring applicaiton, and if so, add them if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties)); } // addCommandLineProperties defaults to true // See if there are any parameters passed in during startup if (this.addCommandLineProperties && args.length > 0) { // COMMAND_LINE_PROPERTY_SOURCE_NAME commandLineArgs String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; // See if the resource contains commandLineArgs if (sources.contains(name)) { // Get commandLineArgs from resource PropertySource<?> source = sources.get(name); // Create a CompositePropertySource object with commandLineArgs as the key CompositePropertySource composite = new CompositePropertySource(name); // Add a PropertySource with springApplicationCommandLineArgs as the key and the passed in parameter as the value to the composite composite.addPropertySource( new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args)); // And add source to the composite composite.addPropertySource(source); // Replace the commandLineArgs in sources with one containing commandLineArgs and springApplicationCommandLineArgs // CompositePropertySource object sources.replace(name, composite); // Summary: simply put, put the command line parameters in the attribute and the spring startup parameters into the composite, and then replace the original parameters in the attribute with the current ones // composite } else { // If only spring starts the parameters passed in, add the commandLineArgs as the key and the parameter as the value to the front of the most sources sources.addFirst(new SimpleCommandLinePropertySource(args)); } } }
- Just now, this process is so complicated. In fact, if you don't pass parameters, the above code will not be executed, so don't worry. It's mainly to add startup parameters to attribute resources
- So far, if there are no parameters in the property resource, there are still four sources in the propertySource, and the two still have no values
- Jump into configureProfiles()
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles); // Traverse the PropertySources from to get the property with the property spring.profiles.active profiles.addAll(Arrays.asList(environment.getActiveProfiles())); // Set the effective profiles in the environment, and multiple profiles can be effective at the same time environment.setActiveProfiles(StringUtils.toStringArray(profiles)); }
-
Here, configure environment (environment, applicationarguments. Getsourceargs()); It has been parsed
-
Then go back to prepareEnvironment() and continue with the next process
ConfigurationPropertySources.attach(environment);
- Inside the attach method
/** * Attach a {@link ConfigurationPropertySource} support to the specified * {@link Environment}. * * Add a ConfigurationPropertySource support to the specified environment * * Adapts each {@link PropertySource} managed by the environment * to a {@link ConfigurationPropertySource} and allows classic * {@link PropertySourcesPropertyResolver} calls to resolve using * {@link ConfigurationPropertyName configuration property names}. * <p> * * Adjust each PropertySource of environment management to ConfigurationPropertySource, * It also allows the classic PropertySourcesPropertyResolver call to solve the use problem * ConfigurationPropertyName Configuration attribute name * * The attached resolver will dynamically track any additions or removals from the * underlying {@link Environment} property sources. * * The attached parser will dynamically track any additions or deletions of the underlying environment attribute source. * * @param environment the source environment (must be an instance of * {@link ConfigurableEnvironment}) * @see #get(Environment) */ // Translation: // Add support for a ConfigurationPropertySource to the specified environment public static void attach(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment); // Gets the PropertySources in the environment MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources(); // ATTACHED_PROPERTY_SOURCE_NAME configurationProperties // Get configurationProperties in sources PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME); // If the sources contain configurationProperties and the source is not up-to-date, remove it if (attached != null && attached.getSource() != sources) { sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); attached = null; } if (attached == null) { // Add configurationProperties of ConfigurationPropertySourcesPropertySource type at the beginning of sources, // Content is the sources in the environment sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources))); } }
- At present, according to my understanding, this binding is to change the type of all sources. This type provides some methods to better track, delete, join and other operations
- Return the prepareEnvironment() process and enter listeners.environmentPrepared(environment); inside
void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } }
- This method is to use eventpublishingrnlistener to call the environmentPrepared function to send events prepared by the environment, thus triggering the listener to execute the method
- Let's see which listeners are triggered
// A total of 7 listeners will be triggered 0 = 1 = 2 = 3 = 4 = 5 = 6 =
1.1 ConfigFileApplicationListener
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { // Load environment post processor List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); // And add yourself, because ConfigFileApplicationListener itself is also the post processor of the environment postProcessors.add(this); // sort AnnotationAwareOrderComparator.sort(postProcessors); // Call their post-processing environment functions respectively for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } }
- Enter loadPostProcessors(); inside
List<EnvironmentPostProcessor> loadPostProcessors() { // Load EnvironmentPostProcessor from spring.factories return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class.class, getClass().getClassLoader()); } // Return a total of 4, plus yourself a total of 5 // 0 = // 1 = // 2 = // 3 = // 4 =
- All right, one by one
- Enter the SystemEnvironmentPropertySourceEnvironmentPostProcessor to see what it does
/** * An {@link EnvironmentPostProcessor} that replaces the systemEnvironment * {@link SystemEnvironmentPropertySource} with an * {@link OriginAwareSystemEnvironmentPropertySource} that can track the * {@link SystemEnvironmentOrigin} for every system environment property. * * An environment post processor is used to replace the attribute resources of the system environment so that it can track and delete what is like before * ConfigurationPropertySources.attach(environment) Role of * Replace the original SystemEnvironmentPropertySource with OriginAwareSystemEnvironmentPropertySource, and the value remains unchanged * @author Madhura Bhave * @since 2.0.0 */ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { /** * The default order for the processor. */ public static final int DEFAULT_ORDER = SpringApplicationJsonEnvironmentPostProcessor.DEFAULT_ORDER - 1; private int order = DEFAULT_ORDER; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"; String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME; // Get 'systemEnvironment' PropertySource<?> propertySource = environment.getPropertySources().get(sourceName); if (propertySource != null) { // Encapsulate the value of systemEnvironment with a class replacePropertySource(environment, sourceName, propertySource); } } @SuppressWarnings("unchecked") private void replacePropertySource(ConfigurableEnvironment environment, String sourceName, PropertySource<?> propertySource) { // Get value Map<String, Object> originalSource = (Map<String, Object>) propertySource.getSource(); // Repackage values SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource); // Replace the original SystemEnvironmentPropertySource in the environment with OriginAwareSystemEnvironmentPropertySource environment.getPropertySources().replace(sourceName, source); } ... ... }
- Enter the second spring application jsonenvironment postprocessor
/** * An {@link EnvironmentPostProcessor} that parses JSON from * {@code spring.application.json} or equivalently {@code SPRING_APPLICATION_JSON} and * adds it as a map property source to the {@link Environment}. The new properties are * added with higher priority than the system properties. * * Translation: * * @author Dave Syer * @author Phillip Webb * @author Madhura Bhave * @author Artsiom Yudovin * @since 1.3.0 */ public class SpringApplicationJsonEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // Get all propertysources MutablePropertySources propertySources = environment.getPropertySources(); // Find out whether there is spring.application.json. If so, parse the value and store it in the environment propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst() .ifPresent((v) -> processJson(environment, v)); } private void processJson(ConfigurableEnvironment environment, JsonPropertyValue propertyValue) { JsonParser parser = JsonParserFactory.getJsonParser(); // Get the value of spring.application.json and convert it into map Map<String, Object> map = parser.parseMap(propertyValue.getJson()); if (!map.isEmpty()) { // Add to environment addJsonPropertySource(environment, new JsonPropertySource(propertyValue, flatten(map))); } } ... ... }
- The simple way to use it is to add a system property of spring.application.json, and then the value is in the form of JSON. You can add the property to the propertysource
- Enter the third cloud foundry vcapenvironment postprocessor
I don't know what this is
- Enter the fourth ConfigFileApplicationListener
// Extract the post processor part of ConfigFileApplicationListener public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered { ... ... @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { addPropertySources(environment, application.getResourceLoader()); } protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { // Add a RandomValuePropertySource to the propertysource RandomValuePropertySource.addToEnvironment(environment); // Create a Loader and execute the load() method new Loader(environment, resourceLoader).load(); } ... ... }
- Enter new Loader() (Loader is the internal class of ConfigFileApplicationListener)
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { this.environment = environment; // Create a placeholder parser with PropertyPlaceholderHelper inside. The default placeholder is ${} this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment); this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(getClass().getClassLoader()); // Load the PropertySourceLoader from spring.factories and load the following two // 0 = Used to load resources with suffix properties // 1 = Used to load resources with the suffix yaml this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); }
- Once the loader is created, use it to load resources
- Enter the inside of load()
void load() { // Filter and load resources according to attributes, etc FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, (defaultProperties) -> { this.profiles = new LinkedList<>(); this.processedProfiles = new LinkedList<>(); this.activatedProfiles = false; this.loaded = new LinkedHashMap<>(); // Initialize configuration initializeProfiles(); while (!this.profiles.isEmpty()) { // Take out the first one Profile profile = this.profiles.poll(); // Determine whether it is the default configuration if (isDefaultProfile(profile)) { // Add configuration to environment addProfileToEnvironment(profile.getName()); } load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); addLoadedPropertySources(); applyActiveProfiles(defaultProperties); }); }
- Enter FilteredPropertySource.apply()
static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties, Consumer<PropertySource<?>> operation) { // Get all propetiesource s. At present, there should be only 6. RandomValuePropertySource has just been added recently MutablePropertySources propertySources = environment.getPropertySources(); // Gets the resource named defaultProperties in the resource PropertySource<?> original = propertySources.get(propertySourceName); if (original == null) { // If not, call the incoming anonymous method directly operation.accept(null); return; } // If so, replace the resource with FilteredPropertySource and add two additional properties, spring.profiles.active and spring.profiles.include, for filtering propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties)); try { operation.accept(original); } finally { propertySources.replace(propertySourceName, original); } }
- To sum up, the configuration file is loaded according to the passed in parameters and the effective profile
- The last one is DebugAgentEnvironmentPostProcessor, which enters the DebugAgentEnvironmentPostProcessor
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { if (ClassUtils.isPresent(REACTOR_DEBUGAGENT_CLASS, null)) { Boolean agentEnabled = environment.getProperty(DEBUGAGENT_ENABLED_CONFIG_KEY, Boolean.class); if (agentEnabled != Boolean.FALSE) { try { Class<?> debugAgent = Class.forName(REACTOR_DEBUGAGENT_CLASS); debugAgent.getMethod("init").invoke(null); } catch (Exception ex) { throw new RuntimeException("Failed to init Reactor's debug agent"); } } } }
- Used to quickly load this class
1.2 AnsiOutputApplicationListener
- Used to configure ansi encoding
1.3 LoggingApplicationListener
- Set the configuration log according to the configured properties
1.4 ClasspathLoggingApplicationListener
- Print the classpath information according to the output level of the log
1.5 BackgroundPreinitializer
- No action will be performed in this event
1.6 DelegatingApplicationListener
- No action will be performed in this event
1.7 FileEncodingApplicationListener
- Judge whether the file type conforms to the configuration according to the configuration