前言
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中和“环境”相关的几个类,代码执行过程中基本都是围绕这几个类运转的。