Environment 的底层实现
Environment 接口的实现类
先来看 Environment 相关类图,对整体有一个了解。
PropertyResolver、Environment、ConfigurableEnvironment 在前面都有提到,剩下比较重要的类如下。
ConfigurablePropertyResolver:PropertyResolver 的子接口,在PropertyResolver 的基础上添加了设置类型转换服务 ConfigurableConversionService 及占位符前缀、后缀等方法。
AbstractEnvironment:Environment 的抽象实现,并且提供了自定义默认属性源的方法,下文将详细对其进行分析。
StandardEnvironment:非 web 环境下的标准实现,默认添加了表示系统属性的 PropertiesPropertySource 和 操作系统环境变量的 SystemEnvironmentPropertySource。
StandardServletEnvironment: web 环境下的标准实现,默认添加了 ServletConfigPropertySource 和 ServletContextPropertySource 两种属性源。
Profiles 管理实现
Environment Profiles 相关方法由 AbstractEnvironment 进行实现。部分代码如下。
public abstract class AbstractEnvironment implements ConfigurableEnvironment { // active profiles 属性名 public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active"; // default profiles 属性名 public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default"; // active profiles 集合 private final Set<String> activeProfiles = new LinkedHashSet<>(); // default profiles 集合 private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles()); // 当前 Environment 接受的 profiles @Override public boolean acceptsProfiles(Profiles profiles) { Assert.notNull(profiles, "Profiles must not be null"); return profiles.matches(this::isProfileActive); } // 给定名称的 profile 是否激活 protected boolean isProfileActive(String profile) { validateProfile(profile); Set<String> currentActiveProfiles = doGetActiveProfiles(); // 激活的 profiles 或默认的 profiles 中存在给定名称的 profile 则表示接受 return (currentActiveProfiles.contains(profile) || (currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile))); } //获取激活的 profiles protected Set<String> doGetActiveProfiles() { synchronized (this.activeProfiles) { if (this.activeProfiles.isEmpty()) { // 优先获取 API 到当前 Environment 中的 profiles, 获取不到则会从属性源中获取 String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { setActiveProfiles(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(profiles))); } } return this.activeProfiles; } } // 获取默认的 profiles protected Set<String> doGetDefaultProfiles() { synchronized (this.defaultProfiles) { if (this.defaultProfiles.equals(getReservedDefaultProfiles())) { // 优先获取 API 到当前 Environment 中的 profiles, 获取不到则会从属性源中获取 String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { setDefaultProfiles(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(profiles))); } } return this.defaultProfiles; } } }
可以看到,Profiles 的数据来源主要包括两块,通过 ConfigurableEnvironment API 设置的 profiles 优先,如果未设置则会从配置属性源中获取,然后进行缓存。
@Profile 注解的实现
Spring 4.0 之后对 @Profile 进行了重构,使用 @Conditional 实现,参见 Spring 条件注解 @Conditional 使用及其底层实现。@Profile 源码如下。
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(ProfileCondition.class) public @interface Profile { /** * The set of profiles for which the annotated component should be registered. */ String[] value(); }
可以看到,这里使用的条件是 ProfileCondition,源码如下。
class ProfileCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) { if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) { return true; } } return false; } return true; } }
ProfileCondition 实现较为简单,如果 Environment 接受 @Profile 的值,则允许组件进行注入。
Environment 属性获取实现
以 AbstractEnvironment#getProperty(String) 获取属性的方法为例,代码如下。
public abstract class AbstractEnvironment implements ConfigurableEnvironment { // 属性源 private final MutablePropertySources propertySources = new MutablePropertySources(); // 属性解析器 private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources); @Override @Nullable public String getProperty(String key) { return this.propertyResolver.getProperty(key); } }
属性源由 MutablePropertySources 进行保存,而属性的获取则委托为 PropertySourcesPropertyResolver,这是 Spring 中常用的一种设计模式,类实现某一个接口,而实现委托给接口的另一个实现完成。再看 PropertySourcesPropertyResolver 源码。
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { // 属性源保存 @Nullable private final PropertySources propertySources; // 构造方法 public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) { this.propertySources = propertySources; } // 设置类型转换服务 @Override public void setConversionService(ConfigurableConversionService conversionService) { this.propertyResolver.setConversionService(conversionService); } //设置占位符前缀 @Override public void setPlaceholderPrefix(String placeholderPrefix) { this.propertyResolver.setPlaceholderPrefix(placeholderPrefix); } // 设置占位符后缀 @Override public void setPlaceholderSuffix(String placeholderSuffix) { this.propertyResolver.setPlaceholderSuffix(placeholderSuffix); } // 获取属性值 @Override @Nullable public String getProperty(String key) { return getProperty(key, String.class, true); } // 获取给定类型的属性值 @Nullable protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { // 循环从属性源中获取属性值 for (PropertySource<?> propertySource : this.propertySources) { ... 省略部分代码 Object value = propertySource.getProperty(key); if (value != null) { // 解析属性值中的占位符 if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); } logKeyFound(key, propertySource, value); // 类型转换 return convertValueIfNecessary(value, targetValueType); } } } ... 省略部分代码 return null; } }
PropertySourcesPropertyResolver 属性解析器循环属性源获取属性,有必要的情况下会解析属性值中的占位符,最后还会进行类型转换。类型转换为委托给 ConversionService,而占位符则有前缀、后缀。这些信息都在 AbstractEnvironment 中进行设置。
@ProperySource 注解的实现
@ProperySource 用来标注在配置类上,因此 @ProperySource 的处理在配置类的解析过程中。具体代码位置在ConfigurationClassParser#processPropertySource,这里简单进行分析。
private void processPropertySource(AnnotationAttributes propertySource) throws IOException { // 先获取 @PropertySource 注解的属性值 String name = propertySource.getString("name"); if (!StringUtils.hasLength(name)) { name = null; } String encoding = propertySource.getString("encoding"); if (!StringUtils.hasLength(encoding)) { encoding = null; } String[] locations = propertySource.getStringArray("value"); Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required"); boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound"); Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory"); // 默认使用 DefaultPropertySourceFactory 创建属性源的实例 PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); for (String location : locations) { try { String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); Resource resource = this.resourceLoader.getResource(resolvedLocation); // 循环添加属性源 addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) { // Placeholders not resolvable or resource not found when trying to open it if (ignoreResourceNotFound) { if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { throw ex; } } } }
处理 @PropertySource 过程,循环将资源文件位置转换为 EncodedResource,然后创建属性源,最后将属性源添加到 Environment 中。如果同时指定的属性源的名称和多个属性源的位置,还会将多个名称相同的属性源组合为一个 PropertySource。这里看一眼 DefaultPropertySourceFactory 的实现。
public class DefaultPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource)); } }
默认创建的 PropertySource 是 ResourcePropertySource,这个属性源会从 properties 或 xml 中加载属性,由于使用了 EncodedResource ,因此可以加载到正确编码的内容而不会乱码。有关 Resource 的文章可见 Spring 资源管理 (Resource)
@Value 如何注入属性
Spring 中 @Autowire 和 @Value 注解的处理都使用 AutowiredAnnotationBeanPostProcessor ,它会在 bean 实例化后对使用 @Autowire、@Value、@Inject 等注解标注的属性赋值,底层实现依赖AutowireCapableBeanFactory#resolveDependency。这块由于比较复杂,后面会再开一篇文章对其分析,感兴趣的小伙伴可先自行阅读源码。
总结
本篇首先对 Environment 的使用进行介绍,然后对其底层的实现进行分析。理解 Environment 同样有助于熟悉 Spring 的第三方框架源码,欢迎大家留言交流,感谢。