程序员社区

Spring 框架源码解读12


title: Spring 框架源码解读12
date: 2020/04/20 14:57


本节内容 & 思考题

  1. 实现 Environment ,Env 是 ApplicationContext 的环境信息,与我们上节说的 @Value 解析没有关系(说没有到也有点关系)
  2. @PropertySource 注解
  3. @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 那节吧

Spring 框架源码解读12插图

我们现在就接着看这段代码

Spring 框架源码解读12插图1
赞(0) 打赏
未经允许不得转载:IDEA激活码 » Spring 框架源码解读12

一个分享Java & Python知识的社区