3.refresh-prepareRefresh方法详解

【接上文】

0 引子

AnnotationConfigApplicationContext中的refresh是调用的AbstractApplicationContext的refresh方法。
AbstractApplicationContext#refresh

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        // 1. Prepare this context for refreshing.
        prepareRefresh();
        // 2. Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 3. Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        try {
            // 4. Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // 5. Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
            // 6. Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            // 7. Initialize message source for this context.
            initMessageSource();
            // 8. Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // 9. Initialize other special beans in specific context subclasses.
            onRefresh();
            // 10. Check for listener beans and register them.
            registerListeners();
            // 11. Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);
            // 12. Last step: publish corresponding event.
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // 13. might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

下面按顺序分析下这13个方法。

1. prepareRefresh();

/**
 * Prepare this context for refreshing, setting its startup date and
 * active flag as well as performing any initialization of property sources.
 * 完成刷新前的操作
 */
protected void prepareRefresh() {
    // Switch to active.
     //获取系统时间、修改状态位标识
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    // Initialize any placeholder property sources in the context environment.
    // 初始化占位符资源,是个模板方法由具体的子类实现 目前的实现类没有做这个方法的实现,也就是默认的什么都不做
    initPropertySources();
    // Validate that all properties marked as required are resolvable:
    // see ConfigurablePropertyResolver#setRequiredProperties
    getEnvironment().validateRequiredProperties();
    // Store pre-refresh ApplicationListeners...
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        // Reset local application listeners to pre-refresh state.
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

顺着线看prepareRefresh里面的getEnvironment().validateRequiredProperties();

AbstractApplicationContext#getEnvironment 339-353

public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
        this.environment = createEnvironment();
    }
    return this.environment;
}

/**
 * Create and return a new {@link StandardEnvironment}.
 * <p>Subclasses may override this method in order to supply
 * a custom {@link ConfigurableEnvironment} implementation.
 */
protected ConfigurableEnvironment createEnvironment() {
   return new StandardEnvironment();
}

可以看到就是new了一个StandardEnvironment对象,顺便看下这个StandardEnvironment

/* That is, if the key "xyz" is present both in the JVM system properties as well as in
 * the set of environment variables for the current process, the value of key "xyz" from
 * system properties will return from a call to {@code environment.getProperty("xyz")}.
 * This ordering is chosen by default because system properties are per-JVM, while
 * environment variables may be the same across many JVMs on a given system.  Giving
 * system properties precedence allows for overriding of environment variables on a
 * per-JVM basis.
 * 如果同一个变量在系统配置和环境变量的配置里面都存在默认使用环境变量覆盖系统变量,原因是系统变量是跟着jvm走的,环境变量可以针对与一个
 * 给定的操作系统。
 */
public class StandardEnvironment extends AbstractEnvironment {

   public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

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

没看到目标方法,看注释这里只是指定了一个同key的覆盖策略。继续看它的父类, 重点关注我们要找的方法,validateRequiredProperties

AbstractEnvironment#validateRequiredProperties

private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
    this.propertyResolver.validateRequiredProperties();
}

很简单,调用了propertyResolver的validateRequiredProperties方法,继续跟这个类的方法调用,发现实际调用的是PropertySourcesPropertyResolver的父类AbstractPropertyResolver中的方法,源码如下:

AbstractPropertyResolver#validateRequiredProperties

private final Set<String> requiredProperties = new LinkedHashSet<>();
@Override
public void validateRequiredProperties() {
    MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
    for (String key : this.requiredProperties) {
        if (this.getProperty(key) == null) {
            ex.addMissingRequiredProperty(key);
        }
    }
    if (!ex.getMissingRequiredProperties().isEmpty()) {
        throw ex;
    }
}

可以看到就是判断数据源中是否存在指定requiredProperties中的所有key,有不存在的key或者值为null则会报异常。spring中的实现中没有对requiredProperties进行更多操作,所以是空的,也就是这里的校验默认情况下并不会报错却可以根据实际情况进行扩展。

【扩展需求】需要在启动的时候判断某个属性配置必须存在才能正常启动,如MYSQL_HOST

【扩展需求实现思路】

  1. 新建一个applicationContext继承自AnnotationConfigApplicationContext或者ClasspathXmlApplicationContext,重写其中的initPropertySources();因为该方法会在校验之前调用,同时在重写的方法中通过setRequiredProperties方法设置需要校验的必有属性即可。
  2. 指定启动时候使用的ApplicationContext为自定义的ApplicationContext即可。

这里我们再延伸关注下这个getProperty方法,看spring究竟是如何读取参数值的。还是AbstractPropertyResolver这个类,
找到getProperty(String key)方法。

AbstractPropertyResolver#getProperty(String key)

@Override
@Nullable
public String getProperty(String key) {
    return getProperty(key, String.class, true);
}

@Override
@Nullable
public <T> T getProperty(String key, Class<T> targetValueType) {
    return getProperty(key, targetValueType, true);
}

@Override
@Nullable
protected String getPropertyAsRawString(String key) {
    return getProperty(key, String.class, false);
}

@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        for (PropertySource<?> propertySource : this.propertySources) {
            if (logger.isTraceEnabled()) {
                logger.trace("Searching for key '" + key + "' in PropertySource '" +
                             propertySource.getName() + "'");
            }
            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);
            }
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Could not find key '" + key + "' in any property source");
    }
    return null;
}

可以看到,实际是从PropertySources中取的,这里的PropertySource是个抽象类,有多个实现子类,
如CommandLinePropertySource, MapPropertySource等。

在PropertySource中只有一个抽象方法public abstract Object getProperty(String name);同时提供了带数据源的构造方法,
也就是说子类只需要定义好数据源,同时实现getProperty(String name)方法就可以用父类完成属性解析的工作了,
当然了spring还提供了另一个基于PropertySource的抽象实现类EnumerablePropertySource,提供了一些额外的扩展方法,具体实现此处不做更深的展开。

至此 getEnvironment().validateRequiredProperties();这个方法的作用和实现过程就分析完了。
prepareRefresh后续就是初始化事件和监听器,此处不再细说。

【小结】

  1. 设置BeanFactory的激活状态
  2. 验证必选属性是否已配置-用户可扩展
  3. 初始化事件列表和事件监听器列表

【NEXT】获取BeanFactory

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泽济天下

你的鼓励是我最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值