title: Spring 框架源码解读12
date: 2020/04/20 14:57
本节内容 & 思考题
- 实现 Environment ,Env 是 ApplicationContext 的环境信息,与我们上节说的 @Value 解析没有关系(说没有到也有点关系)
- @PropertySource 注解
- @Profile 注解
Environment
/**
* Environment 继承了 PropertyResolver,它可以解析我们给他提供的配置
* <p>
* 相比较,Environment 多了所选环境的信息。
*
* @author yujx
* @date 2020/04/20 09:34
*/
public interface Environment extends PropertyResolver {
String[] getActiveProfiles();
String[] getDefaultProfiles();
}
// 通过*组合*的方式进行完成 PropertyResolver 的功能。
public abstract class AbstractEnvironment implements Environment {
// 在 Spring 中采用的是这个对象: private final PropertySources propertySources;
private final List<Properties> propertySources = new ArrayList<>();
private final PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment() {
this.customizePropertySources(this.propertySources);
}
// 子类实现,添加一些 properties
protected void customizePropertySources(List<Properties> propertySources) {
}
@Override
public String[] getActiveProfiles() {
String[] activeProfiles = this.getProperty("activeProfiles", String[].class);
if (ArrayUtil.isNotEmpty(activeProfiles)) {
return activeProfiles;
}
return this.getDefaultProfiles();
}
@Override
public String[] getDefaultProfiles() {
return new String[]{"default"};
}
@Override
public String getProperty(String key) {
return propertyResolver.getProperty(key);
}
@Override
public <T> T getProperty(String key, Class<T> targetType) {
return propertyResolver.getProperty(key, targetType);
}
public void addProperties(Properties properties) {
propertySources.add(properties);
}
}
/**
* 子类负责注入配置
*
* @author yujx
* @date 2020/04/20 13:36
*/
public class StandardEnvironment extends AbstractEnvironment {
public StandardEnvironment() {
super();
}
@Override
protected void customizePropertySources(List<Properties> propertySources) {
// StandardEnvironment 中 Spring 给他添加了这些东西 systemEnvironment、systemProperties
// StandardServletEnvironment 中 Spring 给他添加了这些东西 servletContextInitParams、servletConfigInitParams
// 所以没有使用 SpringBoot 的时候 spring.profiles.active 需要配置在 web.xml 中
/*
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
*/
// application.xml 中引入的 properties 只用于 @Value 的那个后置处理器使用,如果需要在 env 中也使用,需要配合 @PropertiesSources 注解
// <context:property-placeholder location="classpath:db.properties" />
// SpringBoot 中 application.yml 在哪添加的我忘了,反正是获取到了这个 list ,然后直接向里面 add 的。
}
}
application.xml 中引入的 properties 只用于 @Value 的那个后置处理器使用,如果需要在 env 中也使用,需要配合 @PropertiesSources 注解。
@PropertiesSources 注解
1、新增注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// @Repeatable(PropertySources.class) 只带大家实现 @PropertySource 注解,@PropertySources 大家可以自行实现
public @interface PropertySource {
/**
* 文件路径列表
*/
String[] value();
}
2、修改 ConfigurationClassPostProcessor ,对 @PropertySource 注解进行处理
还有实现了 EnvironmentAware
private void processConfigurationClass(BeanDefinitionRegistry registry, BeanDefinition classBeanDefinition, Class<?> clazz) {
...
// 3、处理 @PropertySource 注解
PropertySource propertySource = AnnotationUtil.getAnnotation(clazz, PropertySource.class);
if (ObjectUtil.isNotEmpty(propertySource) && ObjectUtil.isNotEmpty(propertySource.value())) {
this.processPropertySource(propertySource.value());
}
...
}
private void processPropertySource(String[] locations) {
List<Properties> propertySources = environment.getPropertySources();
for (String location : locations) {
// 将文件读入 Properties 中
try {
FileInputStream inputStream = new FileInputStream(location);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
Properties properties = new Properties();
properties.load(inputStreamReader);
propertySources.add(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3、修改 PropertySourcesBeanFactoryPostProcessor
public class PropertySourcesBeanFactoryPostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
// 存放 properties 文件的位置
// 理论上是 Spring 初始化这个对象的时候,会通过 set 方法依赖注入,但是为了省事,直接在这里写死
private String[] locations = new String[]{
"/Users/x5456/IdeaProjects/Summer/src/test/resources/value/test.properties"
};
private Environment environment;
/**
* 向 bf 中添加一个 @Value 解析器
*/
@Override
public void postProcessBeanFactory(ListableBeanFactory beanFactory) {
...
// 如果 applicationContext 中的 env 不为空,则把其中加载的 Properties 也加入
// 通过这段代码可以得出,用 xml 配置的 properties 文件,从 env 中读不出来,但是通过注解 @PropertySource 的两者都能获取到
if (this.environment != null) {
List<Properties> envPropertySources = environment.getPropertySources();
propertiesList.addAll(envPropertySources);
}
// 创建一个 properties 解析器
PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertiesList);
// 将该解析器放进 beanFactory 中
beanFactory.addEmbeddedValueResolver(propertyResolver);
}
实现 @Profile 注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* 当前配置应用于哪些环境
*/
String[] value();
}
public class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotationMetadata metadata) {
String[] activeProfiles = conditionContext.getEnvironment().getActiveProfiles();
String[] profileValue = metadata.getAnnotation(Profile.class).value();
Collection<String> intersection = CollUtil.intersection(Arrays.asList(activeProfiles), Arrays.asList(profileValue));
return CollUtil.isNotEmpty(intersection);
}
}
public class ConditionContext {
private BeanFactory beanFactory;
private Environment environment;
public ConditionContext(BeanFactory beanFactory, Environment environment) {
this.beanFactory = beanFactory;
this.environment = environment;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
Spring 5.0
Env 部分实现和我大同小异
@PropertySource 注解解析
还记的 @Configuration 那节吧
我们现在就接着看这段代码