跟我一起阅读SpringBoot源码(四)——StandardServletEnvironment

前情回顾

前面有讲到ConfigurableEnvironment,然后springBoot启动的时候就会去准备环境,根据webApplicationType选择策略,我们当前的demo中选择的环境类型是servlet,所以我们本文重点来看看StandardServletEnvironment。
在这里插入图片描述

继承与实现关系

我们先看看StandardServletEnvironment的继承关系,最上面是从AbstractEnvironment继承来的:
在这里插入图片描述
根据类的初始化过程,我们先看看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

先看看初始化需要用到的参数MutablePropertySources是什么意思:
在这里插入图片描述
MutablePropertySources类中包含一个存放PropertySource的集合,然后提供了这个集合的操作方法。应该就是为后面读取Property准备的。

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

自定义属性源

StandardEnvironment#customizePropertySources

接下来看看customizePropertySources是怎么操作的,可以怎么玩,根据继承关系,先执行的是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()));
	}

可以看到,先加载的是JVM系统配置的属性源,然后才是系统里面配置的。按照AbstractEnvironment的规则,JVM里面的配置优先级高于系统配置。

getSystemEnvironment

先看看系统环境怎么获取的:

@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;
					}
				}
			};
		}
	}

小知识又多了点:
可以通过配置来控制springBoot是否读取系统环境:suppressGetenvAccess
可以通过配置spring.getenv.ignore为true来避免读取系统环境配置。在这里插入图片描述
不过spring.getenv.ignore的值默认是false,并且也没遇到过哪里配置成true的。[手动捂脸]
在这里插入图片描述

看下System.getenv();能得到啥就不说了,自己电脑跑下就可以了。

getSystemProperties

再看下从jvm系统里面读属性又读了些啥嘛:

@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;
					}
				}
			};
		}
	}

好像也没啥东西。过……

StandardServletEnvironment#customizePropertySources

再来看看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);
	}

根据规则,属性配置源的优先级:servletContextInitParams > servletConfigInitParams > jndiProperties > systemProperties > systemEnvironment;
加接来再看看servletContextInitParams 、servletConfigInitParams 、 jndiProperties 的配置属性;
servletContextInitParams和servletConfigInitParams 的配置对象都是通过初始化StubPropertySource来配置的,那就先看看StubPropertySource的注释。
在这里插入图片描述
也就是说目前还不能确定配置的值,先用个属性源占个坑,然后等上下文刷新的时候再用值来替换。好吧,那就先看看jndiProperties 的配置吧。
是否启动JNDI配置的开关:

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";

这里默认是false。
对于JNDI是什么,我也不是很了解,只是在以前的工作中用过,当时是把JNDI配置到Tomcat中,然后程序里面通过JNDI的配置来获取数据源,后面用了Spring后就没遇到过了。这里找了篇文章,可以参考下。JNDI学习总结(一):JNDI到底是什么?

猜你喜欢

转载自blog.csdn.net/qq1049545450/article/details/113110816