Read the SpringBoot source code with me (4)-StandardServletEnvironment

Review

As mentioned earlier ConfigurableEnvironment, when springBoot starts, it will prepare the environment. According to the webApplicationTypeselection strategy, the environment type selected in our current demo is servlet, so we will focus on StandardServletEnvironment in this article.
Insert picture description here

Inheritance and realization

Let's take a look at the inheritance relationship of StandardServletEnvironment. The top is inherited from AbstractEnvironment:
Insert picture description here
According to the initialization process of the class, let's first look at the parameterless construction method of AbstractEnvironment:

    /**
     * 自定义在调用getProperty(String)和相关方法期间此Environment要搜索的PropertySource对象集。
     * 鼓励重写此方法的子类使用MutablePropertySources.addLast(PropertySource)添加属性源,以使其他子类可以调用具有可预测结果的super.customizePropertySources() 。 例如:
     * 	   public class Level1Environment extends AbstractEnvironment {
     *                      @Override
     *           protected void customizePropertySources(MutablePropertySources propertySources) {
     * 	           super.customizePropertySources(propertySources); // no-op from base class
     * 	           propertySources.addLast(new PropertySourceA(...));
     * 	           propertySources.addLast(new PropertySourceB(...));
     *           }
     * 	   }
     *
     * 	   public class Level2Environment extends Level1Environment {
     *           @Override
     *           protected void customizePropertySources(MutablePropertySources propertySources) {
     * 	           super.customizePropertySources(propertySources); // add all from superclass
     * 	           propertySources.addLast(new PropertySourceC(...));
     * 	           propertySources.addLast(new PropertySourceD(...));
     *           }
     * 	   }
     *
     * 在这种安排下,将按照源A,B,C,D的顺序解析属性。 也就是说,属性源“ A”优先于属性源“ D”。 如果Level2Environment子类希望为属性源C和D提供比A和B更高的优先级,则可以在之后而不是在添加其自身之前简单地调用super.customizePropertySources :
     * 	   public class Level2Environment extends Level1Environment {
     *           @Override
     *           protected void customizePropertySources(MutablePropertySources propertySources) {
     * 	           propertySources.addLast(new PropertySourceC(...));
     * 	           propertySources.addLast(new PropertySourceD(...));
     * 	           super.customizePropertySources(propertySources); // add all from superclass
     *           }
     * 	   }
     *
     * 现在,根据需要,搜索顺序为C,D,A,B。
     * 除了这些建议之外,子类还可以使用任何add* , remove或replace MutablePropertySources公开的方法,以创建所需属性源的精确排列。
     * 基本实现未注册任何属性源。
     * 请注意,任何ConfigurableEnvironment客户端都可以通过getPropertySources()访问器(通常在ApplicationContextInitializergetPropertySources()进一步定制属性源。 例如:
     * 	   ConfigurableEnvironment env = new StandardEnvironment();
     * 	   env.getPropertySources().addLast(new PropertySourceX(...));
     *
     * 有关实例变量访问的警告
     * 在子类中具有默认的初始值声明实例变量不应该从这个方法中访问。 由于Java对象创建生命周期的限制,当AbstractEnvironment()构造函数调用此回调时,尚未分配任何初始值,这可能导致NullPointerException或其他问题。 如果需要访问实例变量的默认值,请将此方法保留为空操作,并直接在子类构造函数中执行属性源操作和实例变量访问。 注意,给实例变量赋值是没有问题的。 它只是试图读取必须避免的默认值。
     * @see MutablePropertySources
	 * @see PropertySourcesPropertyResolver
	 * @see org.springframework.context.ApplicationContextInitializer
     */
	protected void customizePropertySources(MutablePropertySources propertySources) {
	}

MutablePropertySources

Let's take a look at the MutablePropertySourcesmeaning of the parameters needed for initialization : The
Insert picture description here
MutablePropertySources class contains a collection of PropertySources, and then provides the operation methods of this collection. It should be prepared for later reading Property.

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();

Custom attribute source

StandardEnvironment#customizePropertySources

Next, take a look at how customizePropertySources is operated and how you can play. According to the inheritance relationship, the first thing to do is org.springframework.core.env.StandardEnvironment#customizePropertySources:

	/** 系统环境属性源名称 */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM系统属性源名称*/
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

/**
	 * 使用适用于任何标准Java环境的属性定制属性源集:
	 * “系统属性”
	 * “ systemEnvironment”
	 * 目前在属性“systemProperties”将优先于“systemEnvironment” 。
	 * @see AbstractEnvironment#customizePropertySources(MutablePropertySources)
	 * @see #getSystemProperties()
	 * @see #getSystemEnvironment()
	 */
	@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()));
	}

It can be seen that the attribute source of the JVM system configuration is loaded first, and then the configuration in the system. According to the rules of AbstractEnvironment, the priority of the configuration in the JVM is higher than the system configuration.

getSystemEnvironment

First look at how the system environment is obtained:

@Override
	@SuppressWarnings({"rawtypes", "unchecked"})
	public Map<String, Object> getSystemEnvironment() {
		if (suppressGetenvAccess()) {
			return Collections.emptyMap();
		}
		try {
			return (Map) System.getenv();
		}
		catch (AccessControlException ex) {
			return (Map) new ReadOnlySystemAttributesMap() {
				@Override
				@Nullable
				protected String getSystemAttribute(String attributeName) {
					try {
						return System.getenv(attributeName);
					}
					catch (AccessControlException ex) {
						if (logger.isInfoEnabled()) {
							logger.info("Caught AccessControlException when accessing system environment variable '" +
									attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
						}
						return null;
					}
				}
			};
		}
	}

There are more little knowledge:
you can control whether springBoot reads the system environment
through configuration : suppressGetenvAccess can be configured spring.getenv.ignoreto true to avoid reading the system environment configuration. Insert picture description here
However spring.getenv.ignore, the default value is false, and I have not encountered any configuration as true. [Manually cover your face]
Insert picture description here

Let's System.getenv();not say what you can get, just run it on your own computer.

getSystemProperties

Let's take a look at what we read when reading attributes from the jvm system:

@Override
	@SuppressWarnings({"rawtypes", "unchecked"})
	public Map<String, Object> getSystemProperties() {
		try {
			return (Map) System.getProperties();
		}
		catch (AccessControlException ex) {
			return (Map) new ReadOnlySystemAttributesMap() {
				@Override
				@Nullable
				protected String getSystemAttribute(String attributeName) {
					try {
						return System.getProperty(attributeName);
					}
					catch (AccessControlException ex) {
						if (logger.isInfoEnabled()) {
							logger.info("Caught AccessControlException when accessing system property '" +
									attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
						}
						return null;
					}
				}
			};
		}
	}

There seems to be nothing. Over...

StandardServletEnvironment#customizePropertySources

Take a look again org.springframework.web.context.support.StandardServletEnvironment#customizePropertySources:

	/** Servlet上下文初始化参数属性源名称:“ servletContextInitParams” */
	public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

	/** Servlet配置初始化参数属性源名称:“ servletConfigInitParams”。*/
	public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

	/** JNDI属性源名称:“ jndiProperties”。 */
	public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

/**
	 * 使用超类提供的属性源以及适用于基于标准servlet的环境的属性源来定制属性源集:
     * “ servletConfigInitParams”
     * “ servletContextInitParams”
     * “ jndiProperties”
     * 在本属性“servletConfigInitParams”将接管那些优先在“servletContextInitParams” ,并且在任一上述优先于那些在发现的发现属性“jndiProperties” 。
     * 以上任何一项中的属性都将优先于StandardEnvironment超类提供的系统属性和环境变量。
     * 与Servlet相关的属性源在此阶段作为stubs添加,并且在实际的ServletContext对象可用时将被完全初始化。
	 * @see StandardEnvironment#customizePropertySources
	 * @see org.springframework.core.env.AbstractEnvironment#customizePropertySources
	 * @see ServletConfigPropertySource
	 * @see ServletContextPropertySource
	 * @see org.springframework.jndi.JndiPropertySource
	 * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources
	 * @see #initPropertySources(ServletContext, ServletConfig)
	 */
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		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);
	}

According to the rules, the priority of the attribute configuration source: servletContextInitParams> servletConfigInitParams> jndiProperties> systemProperties> systemEnvironment;
add it and take a look at the configuration properties of servletContextInitParams, servletConfigInitParams, jndiProperties; the configuration properties of
servletContextInitParams and InitParams are initialized by servletConfig. Then look at the comments of StubPropertySource first.
Insert picture description here
That is to say, the value of the configuration is not yet determined. First use an attribute source to occupy a hole, and then replace it with the value when the context is refreshed. Well, let's take a look at the configuration of jndiProperties first.
Whether to start the switch of JNDI configuration:

JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable())

/**
	 *检查在此JVM上是否有默认的JNDI环境(如Java EE环境)。
	 * @return {@code true} if a default InitialContext can be used,
	 * {@code false} if not
	 */
	public static boolean isDefaultJndiEnvironmentAvailable() {
		if (shouldIgnoreDefaultJndiEnvironment) {
			return false;
		}
		try {
			new InitialContext().getEnvironment();
			return true;
		}
		catch (Throwable ex) {
			return false;
		}
	}
private static final boolean shouldIgnoreDefaultJndiEnvironment = SpringProperties.getFlag(IGNORE_JNDI_PROPERTY_NAME);
/**
指示Spring忽略默认JNDI环境的系统属性,即始终从isDefaultJndiEnvironmentAvailable()返回false 。
默认值为“ false”,允许常规的默认JNDI访问,例如在JndiPropertySource 。 将此标志设置为true是一种优化方案,该方案从一开始就找不到此类JNDI后备搜索,从而避免了重复的JNDI查找开销。
请注意,此标志仅影响JNDI后备搜索,而不影响显式配置的JNDI查找(如针对DataSource或其他环境资源的查找)。 从字面上看,该标志仅影响尝试基于JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()检查进行JNDI搜索的代码:特别是StandardServletEnvironment和StandardPortletEnvironment 
*/
public static final String IGNORE_JNDI_PROPERTY_NAME = "spring.jndi.ignore";

The default here is false.
I don’t know what JNDI is. I just used it in my previous work. At that time, JNDI was configured in Tomcat, and then the data source was obtained through JNDI configuration in the program. After using Spring, I didn’t encounter it. pass. Find an article here, you can refer to it. JNDI learning summary (1): What is JNDI?

Guess you like

Origin blog.csdn.net/qq1049545450/article/details/113110816