The article directories

  • One, foreword
  • Ii. Introduction to Spring Environment
    • 2.1 Env profiles different environment configuration groups
      • 2.1.1 Env profiles are basically used
      • 2.1.2 Three places to configure the environment: startup class, application.properties Configuration file, and Run/Debug Configuration
      • 2.1.3 The setActiveProfiles() parameter is an array and you can set more than one
    • @value (“{XXX}”)
      • 2.2.1 Retrieving Common Configurations using @value
      • 2.2.2 Using @value to Retrieve system configuration, such as java.version
      • 2.2.3 Fetching system configuration with Environment instance bean, such as java.version
      • 2.2.4 Why can we use Environment instance bean to fetch java.version system configuration
  • Spring Environment source code analysis
    • 3.1 Start with the run() method
    • 3.2 First submethod: Create or get the getOrCreateEnvironment() method
      • 3.2.1 AbstractEnvironment class
      • 3.2.2 StandardEnvironment class
      • 3.2.3 StandardServletEnvironment class
      • 3.2.4 MutablePropertySources class
    • 3.3 Second submethod: Prepare the configureEnvironment()
      • 3.3.1 configureEnvironment in The SpringApplication Class ()
      • 3.3.2 Parsing properties: SpringApplication class configurePropertySources()
      • 3.3.3 SpringApplication Class configureProfiles()
    • 3.4 third son methods: start loading springboot configuration listeners. EnvironmentPrepared (environment)
      • 3.4.1 track listeners. EnvironmentPrepared (environment)
      • 3.4.2 onApplicationEnvironmentPreparedEvent ()
      • RandomValuePropertySource class
  • Fourth, the end

One, foreword

Ii. Introduction to Spring Environment

2.1 Env profiles different environment configuration groups

2.1.1 Env profiles are basically used

First, we know the environment information in which the entire Spring application runs: Profiles + Properties

First look at profiles, which is configured in the code as:

spring.profiles.active=prd/test/dev...
Copy the code

Profiles functions to logically group beans

Define a ProfileService class with a private property profile as follows:

Create a New ProfileConfiguration configuration class and define the ProfileService instance bean here. When the SpringBoot project starts, the ProfileService instance bean will be loaded into the IOC container. Here’s the whole thing:

On each bean in the ProfileConfiguraiton configuration class, add the @profile annotation to simulate setting up beans for two different environments and declare beans for different environments

Goldfinger: Use @Profile to logically distribute XML and annotations for different beans

Before starting, set the profile in the Environment, setActiveProfile(). Nib is an array, which can be set to hide, for testing purposes. Set “PRD” first, and context.getBean will fetch the bean from the IOC container and print it to the console.

In general, both getBean and @AutoWired can fetch beans from an IOC container.

Run the SpringBoot project, and if you print beans in the IOC container, beans in the PRD environment, it’s ok.

At this point, profiles for env has been set successfully.

2.1.2 Three places to configure the environment: startup class, application.properties Configuration file, and Run/Debug Configuration

We can configure the environment in more than one place, not only in the startup class as above, in the application.properties Configuration file, but also in the Run/Debug Configuration



2.1.3 The setActiveProfiles() parameter is an array and you can set more than one

The setActiveProfiles() argument is an array and can be set to more than one, as follows:



This completes the tutorial on Env profiles.

@value (“{XXX}”)

2.2.1 Retrieving Common Configurations using @value

Create a new UserController class and print the env property in the application.properties configuration file as follows:



The env attribute in the application.properties configuration file is “Hello world” and configured as follows:

Run successfully, retrieved as follows:

So, we see that properties holds the environment or file information for the property.

2.2.2 Using @value to Retrieve system configuration, such as java.version

In fact, the @value annotation can also access information about system environment variables, such as java.version. Take the current JDK version and try it:

Look, it’s printed.

2.2.3 Fetching system configuration with Environment instance bean, such as java.version

We use @autowired to fetch the Environment instance bean from the Spring IOC container, and then directly fetch the Environment instance bean (without @value) as follows:

There are two ways to fetch instance beans from the Spring IOC container, the getBean method or the @AutoWired annotation.



Look, it’s printed, too.

2.2.4 Why can we use Environment instance bean to fetch java.version system configuration

Why is it possible to use Environment to retrieve java.version? Take a look at the Environment interface in Spring, as follows:



The reason is simple, because the Environment instance bean is already in the IOC container, so it is easy to pull out the system configuration java.version, because the system configuration java.version is written to the Environment instance bean.

Others, such as BeanFactory and ApplicationContext instance beans, are also in the IOC container and implement the Environment interface, as follows:

In fact, as the built-in interface of the Spring container, the configuration of the Environment comes from many sources: System Environment variables (such as java.version mentioned above), System variables system.propreties, etc.

Generally speaking, configuration sources in Environment can be classified into two categories: system attribute source + Springboot attribute source, as follows:



This diagram is important and will be used for the rest of this article to show how Environment handles “system property sources” and “Springboot property sources”.

Goldfinger: The SpringBoot startup class automatically scans for Spring + SpringMVC annotations under the package and its subpackages, but does not scan for @mapper annotations in IBatis

Spring Environment source code analysis

Find the Environment interface first

3.1 Start with the run() method

Find springboot project run method, as follows:



CTRL + left to enter a meaningful run method like this:



In the run method that really makes sense, we use the prepareEnvironment method to get an env instance and then set it to the context as follows:

Goldfinger: In source code, when spring or SpringBoot is initialized, env precedes context. Here is the source code of Spring boot, in fact, the source code of Spring is the same.

Select the prepareEnvironment method



Create or obtain an environment variable using the getOrCreateEnvironment method and place the env instance into the configureEnvironment method to complete the configuration. Finally by listeners. EnvironmentPrepared method, the env instance to listen for an event, as follows:



Next take a look at the three child methods of the prepareEnvironment() method of the SpringApplication class.

3.2 First submethod: Create or get the getOrCreateEnvironment() method

As you can see, if the class variable env is not null, it is returned directly. If it is null, a new class instance is returned based on the webApplicationType instance.

So there are three classes involved in the getOrCreateEnvironment() method: StandardEnvironment StandardServletEnvironment StandardReactiveWebEnvironment, these three classes inherit AbstractEnvironment, AbstractEnvironment inherits the Environment interface as follows:

See AbstractEnvironment first class

3.2.1 AbstractEnvironment class

In the AbstractEnvironment constructor, call a custom attribute source method as follows:



The custom attribute source method, which is null in AbstractEnvironment, can only be subclassed.

It is worth noting that for the AbstractEnvironment constructor,

public AbstractEnvironment() {
	customizePropertySources(this.propertySources);
}
Copy the code

This is a nice design to hand off the customizePropertySources implementation to subclasses, which is the template pattern.

So that’s the AbstractEnvironment class, so let’s look at its subclass, the StandardEnvironment class.

3.2.2 StandardEnvironment class

Since the AbstractEnvironment class’s customizePropertySources() is an empty implementation, look at its subclass, the StandardEnvironment class.

StandardEnvironment class has only one customizePropertySources method, also finished, and the StandardServletEnvironment class.

3.2.3 StandardServletEnvironment class

Go directly to the customizePropertySources method implementation as follows:



For the customizePropertySources() method, customizePropertySources() takes a mutable parameter source as input.

This method is null in the AbstractEnvironment class, and in the StandardEnvironment class to put system configurations and system environment variables into env, StandardServletEnvironment class for the servlet configuration properties, the servlet context attributes, JNDI properties, and invoke the StandardEnvironment customizePropertySources () method, So there are servlet configuration properties, Servlet context properties, JNDI properties, system configuration, and system environment variables at the same time.

The beauty of source code design 1: StandardServletEnvironment customizePropertySources in class () method calls the StandardEnvironment customizePropertySources () method, This is where you put your system configuration and environment variables into env. StandardEnvironment’s customizePropertySources() method does not call The AbstractEnvironment’s customizePropertySources() method because it makes the inner method body empty. No need to call; This shows the beauty of the source code.

Source code design beauty 2:

public AbstractEnvironment() {
 		customizePropertySources(this.propertySources); 
} 
Copy the code

The AbstractEnvironment class constructor calls customizePropertySources(), and then the customizePropertySources() layer by layer is rewritten to create a concrete object in the context of the final subclass. This shows the source code design is beautiful.

3.2.4 MutablePropertySources class

CustomizePropertySources () takes the mutable argument source as input and is essentially a MutablePropertySources class object. Let’s look at the MutablePropertySources class, which is defined as follows:



Let’s take a look at StandardServletEnvironment add three properties of is what



When we use Spring + SpringMVC, we need to configure a web.xml, In web.xml, you need to configure two tags < context-params>< /context-params> and < init-params>< /init-params>, and we just used it like this, and in fact, The two tags in web.xml are configured as attributes in env.

Public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = “servletContextInitParams”; < context-params>< /context-params> public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = “ServletConfigInitParams”; < init-params>< /init-params>

We know StandardEnvironment: system variable + system environment variable

/** 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";
Copy the code

Above, the former represents the system environment variable, so we just accessed the system environment variable with @value (“${java.version}”), and the latter represents the system property.

We now complete the figure, a figure summary source structure:

Last question, how to determine the configuration order and priority?

Answer: whether StandardEnvironment classes and class or StandardServletEnvironment, are using addLast add attribute, the order of all attributes is the source code from top to bottom.



All right,ConfigurableEnvironment environment = getOrCreateEnvironment();This line of code, return to SpringApplication class, see configureEnvironment (environment, applicationArguments getSourceArgs ()); This line of code.

3.3 Second submethod: Prepare the configureEnvironment()

3.3.1 configureEnvironment in The SpringApplication Class ()

ConfigureEnvironment () is executed after creating/retrieving an env instance as follows:



In the configureEnvironment() method, the first argument is the ConfigurableEnvironment class object, which is the env object. The second parameter is a class object DefaultApplicationArguments. GetSourceArgs (), it is new in the source code, the following (short answer to understand good, as long as know the command line parameters can be identified as well) :



To get to the point, see the configureEnvironment method as follows:

The class variable addConversionService defaults to true,

private boolean addConversionService = true;
Copy the code

It represents a universal conversion, and when true, executes the if block, gets a conversionService, and sets it to the Spring framework env.

Further down, you can see that the configureEnvironment method executes two methods in sequence: configurePropertySources(Environment, ARGS); And configureProfiles (environment, args); Is the environment information for the entire Spring application to run: Profiles + Properties, which is resolved in these two methods.

3.3.2 Parsing properties: SpringApplication class configurePropertySources()

See the configurePropertySources() method, which resolves all properties properties configured by the developer.

If the default attribute is not empty, add it to sources. AddLast is added at the end of the list. If an argument is passed to the command line at startup, add it too.

In summary, continue to update the env diagram

Adding a command-line argument to env is straightforward, but what is the default argument defaultProperties? Where did it come from? Let’s look at the definition of defaultproperties, which is a map structure by default in the SpringApplication class, as follows:



It is worth noting that this defaultProperties is only available in Spring Boot, not Spring.

For this defaultProperties, you can set setDefautProperties yourself, try it out, as in the springBoot boot class below.

Run it up, look, in the source breakpoint, actually go to this place, pull out the defaultProperties variable, and see the properties that I added manually.



Now that the configurePropertySources() method is complete, let’s look at the configureProfiles() method.

3.3.3 SpringApplication Class configureProfiles()

Create a new LinkedHashSet based on additionalProfiles, and place all the profiles properties in the env variable into this set. Turn the set into a string and put it in env.

Ok, let’s go to the setActiveProfiles() method, as follows:



SetActiveProfiles () sets the profile in env



As for the configureProfiles method, doesn’t that seem to make any sense? Put profiles in an env in a set, and then set in an env.

Note, however, that the setActiveProfiles() method in the AbstractEnvironment class accepts a String mutable array and puts the String profile into a Set of activeProfiles. Because sets can be multiple, they cannot have the same string name.

In fact, we used this method from the very beginning, setting a profile in env that takes an array of two strings “PRD” and “env”, ensuring that they are both arrays and have no duplicate names.

In fact, it is set in the application.properties file as well as in Run/Debug Configurations, where setActiveProfiles() is called.

3.4 third son methods: start loading springboot configuration listeners. EnvironmentPrepared (environment)

3.4.1 track listeners. EnvironmentPrepared (environment)

As a refresher, the common listening logic in Spring is: define an event class, the event publisher publishes a custom event, and the time receiver Listener receives the custom event and its subclasses.

Enter the listeners. EnvironmentPrepared (environment);



Enter the environmentPrepared method as follows:

Take a look at the specific logic of broadcast events as follows:



Enter the invokeListener method as follows:

This listener. OnApplicationContext (event) is said after listening to the event trigger eavesdroppers to complete the corresponding logic.

Ok, let’s review the call relationship as follows:



The onApplicationEvent method has several methods, such as the following:



Arbitrary find a, here ConfigFileApplicationListener class, you can see for through the event types, performs a different operation, are as follows:

The second method for onApplicationEvent onApplicationPreparedEvent, as follows:



EnvironmentPostProcessor has a number of implementation classes, as follows:



Find a few EnvironmentPostProcessor implementation class, choose SpringApplicationJsonEnvironmentPostProcessor kind here, this class, the two interfaces, The EnvironmentPostProcessor interface is extended and the Ordered interface is Ordered, as follows:

3.4.2 onApplicationEnvironmentPreparedEvent ()

The first method about onApplicationEvent onApplicationEnvironmentPreparedEvent () method, as follows:

Enter the postProcessEnvironment method again as follows:

Ok, sort out the call relationship as follows:

Proceed to the addPropertySources method as follows:

We have noticed that in ConfigFileApplicationListener class, there are three important class variables, DEFAULT_PROPERTIES default attributes, DEFAULT_SEARCH_LOCATIONS Default scan location (where the developer’s files should be stored), DEFAULT_NAMES default name (where the developer’s files should be stored), as follows:

In the addPropertySources method, there are three steps, first add, then create a new Loader, and finally execute the load method as follows:

The first step, first add, enter RandomValuePropertySource. AddToEnvironment method, as follows:

RandomValuePropertySource class

Let’s meet this RandomValuePropertySource class, annotation on the class and tell us how to configure, as follows:

There are two constants in the class. The default random attribute is named random and prefixed with random., as follows:

GetProperty: Returns null if name is not prefixed with random. When name is prefixed with random., the getRandomValue method is called to return a random value.

The getRandomValue method returns a random value based on the prefix length of the string argument name.



Ok, we according to the configuration of RandomValuePropertySource class automatically to give it a try, equipped with a name for randomLong, value is random. The long, The bottom is generated by RandomValuePropertySource getRandomValue method of a class, the configuration is as follows:

The main code retrieves the attribute Value via @value or the Environment bean instance, which is used as follows:



Run, really get it out

The refresh changes again and again, each time generating a random long, haha



Ok, play enough, back to ConfigFileApplicationListener addPropertySources method of a class: to add, the new Loader, perform the load method.

Go to the addToEnvironment() method, where addAfter just adds the random property.



Env as shown in figure:

Back to ConfigFileApplicationListener class, check the new Loader (environment, resourceLoader). The load ();

Loader is a resource file Loader



The Loader class constructor sets four class attributes as follows:



See the fourth sentence, as follows:

this.propertySourceLoaders = SpringFactoriesLoader.
     loadFactories(PropertySourceLoader.class,getClass().getClassLoader());
Copy the code

To see the first SpringFactoriesLoader loadFactories method, this method is loaded spring. The factories in the configuration file data,

Looking at the PropertySourceLoader interface itself, this interface contains only two methods, a method to get file extensions and a method to load.

Look at the load method

Enters FilteredPropertySource. The apply () method, which is as follows:

FilteredPropertySource.java static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties, Consumer<PropertySource<? >> operation) { In variable attributes in the source propertySources MutablePropertySources propertySources = environment. GetPropertySources (); // in the variable PropertySource propertySources, get the propertySourceName PropertySource<? > original = propertySources.get(propertySourceName);if(original == null) { operation.accept(null); // If null is retrieved, execute operation.acceptreturn; } // If propertySourceName is not null, the propertySourceName exists. Replace (propertySourceName, new FilteredPropertySource(original, filteredProperties)); try { operation.accept(original); } finally { propertySources.replace(propertySourceName, original); }}Copy the code





Go back to the load method and see the initializeProfiles method



Enter the initializeProfiles method, which initializes the Profiles property with the following logic:



Enter the getOtherActiveProfiles method as follows:

After going through the Loader constructor and having the Loader object, look at the load method as follows:



Enter the load method as follows:



Debug executes as follows:





Enter the loadForFileExtension method as follows:







Fourth, the end

Spring Environment complete parsing, complete.

Play code every day, progress every day!!