SpringBoot源码深度解析(二):SpringBoot环境准备及配置文件解析

前言

SpringBoot中有一个“环境”的概念,这个环境怎么理解呢?可以将其细化为两个更细粒度的概念:profiles和properties。

profile是指一个bean定义组,仅当指定的profile处于激活状态时才会向容器中注册。

properties就是我们经常接触的配置(属性),配置的来源有很多,如:properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps等。

在SpringBoot中,“环境”概念有一个对应的接口Environment,用于处理profiles。同时,它还继承了PropertyResolver接口,这个接口是用来处理property的,比如最基本的根据key获取属性值方法getProperty。于是Environment接口就具备了处理profiles和property的能力,下面的实现类均实现了这个类。

public interface Environment extends PropertyResolver {

	String[] getActiveProfiles();

	String[] getDefaultProfiles();

	boolean acceptsProfiles(Profiles profiles);
}
public interface PropertyResolver {
	boolean containsProperty(String key);

	String getProperty(String key);

	String getProperty(String key, String defaultValue);

	@Nullable
	<T> T getProperty(String key, Class<T> targetType);

	<T> T getProperty(String key, Class<T> targetType, T defaultValue);

	String getRequiredProperty(String key) throws IllegalStateException;

	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
	// 处理 ${...} 表达式
	String resolvePlaceholders(String text);

	// 处理 ${...} 表达式
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}

读过Spring源码的同学都知道,Spring的接口设计层次很多,但是有规律。一般顶层接口规定了最基本的行为,然后定义一些getXXX方法,然后紧接着有一个子接口ConfigurableXXX,里面一般定义的是setXXX,addXXX方法。然后是一个抽象类AbstractXXX,实现了公共的功能并预留了模板方法,供子类实现。

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
	// 激活的Profiles 
	private final Set<String> activeProfiles = new LinkedHashSet<>();
	// 默认的Profiles 
	private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
	// 属性源,通过持有MutablePropertySources对象来实现处理属性源的功能 
	private final MutablePropertySources propertySources = new MutablePropertySources();
	// 属性处理器,PropertySourcesPropertyResolver这个类才是真正实现了顶层接口PropertyResolver中定义的功能,当前类AbstractEnvironment持有这个类的对象来间接实现功能。 
	private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);
	// 构造方法调用了一个模板方法,供子类实现,用来添加数据源
	public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}
	// 本类的获取属性方法其实是调用的PropertySourcesPropertyResolver类的方法
	@Override
	public String getProperty(String key) {
		return this.propertyResolver.getProperty(key);
	}
	// 一系列get set方法不再展示	
	setXXX
	getXXX
}

再来看一个子类StandardEnvironment,这个类是最基础的环境实现类,实现了父类中的模板方法customizePropertySources,往属性源集合中添加2个数据源,这2个分别是 系统属性 和 JVM属性。

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) {
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}
}

再来看一个web应用下的“环境”子类StandardServletEnvironment,它同样实现了模板方法customizePropertySources,往属性源集合中添加ServletContext和ServletConfig两个属性源。

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {

	/** Servlet context init parameters property source name: {@value}. */
	public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

	/** Servlet config init parameters property source name: {@value}. */
	public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

	/** JNDI property source name: {@value}. */
	public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

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

	@Override
	public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
		WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
	}
}

上面就结束了Environment接口体系的介绍,接下来看看AbstractEnvironment持有的2个对象:MutablePropertySources和PropertySourcesPropertyResolver。

1、MutablePropertySources持有一个数据源List属性,和一系列操作List的方法。

public class MutablePropertySources implements PropertySources {
	// 属性是一个List,里面是一个一个的数据源
	private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
	// 由于属性源是有顺序的,所以需要
	public void addFirst(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(0, propertySource);
		}
	}
	public void addLast(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(propertySource);
		}
	}
}

再来看看属性源类PropertySource长啥样,它是个抽象类,有很多具体的属性源子类实现了抽象方法getProperty。查找属性值最终就是遍历这些属性源,然后调用getProperty方法。

public abstract class PropertySource<T> {
	// 属性源名称
	protected final String name;
	// 属性源的真实数据,一般传入的是Map类型,保存的是真实的属性值
	protected final T source;
	// 构造方法
	public PropertySource(String name, T source) {
		this.name = name;
		this.source = source;
	}
	// 抽象方法,供子类实现
	@Nullable
	public abstract Object getProperty(String name);
}

2、PropertySourcesPropertyResolver类是真正查找数据源的类。

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
	// 持有数据源集合
	private final PropertySources propertySources;
	// 初始化时传入数据源集合
	public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
		this.propertySources = propertySources;
	}
	// 真正查找属性的实现方法
	@Nullable
	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources != null) {
			// 遍历属性源集合
			for (PropertySource<?> propertySource : this.propertySources) {
				// 调用属性源的getProperty方法
				Object value = propertySource.getProperty(key);
				// 按需转换value
				if (value != null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		return null;
	}

至此就讲完了SpringBoot中和“环境”相关的几个类,代码执行过程中基本都是围绕这几个类运转的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值