Spring前置准备(三)——校验必须的属性,命令行,环境变量等信息

部署运行你感兴趣的模型镜像

接下来解析refresh() 方法,这个方法是重点,内容如下:

refresh@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        prepareRefresh();

        // 设置refreshed字段为true,refreshed字段的作用目前还没找到
        // 返回当前工厂对象
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 填充bean工厂的一些基本参数
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            // 在上下文中调用作为 Bean 注册的工厂处理器。
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // 检查侦听器 bean 并注册它们。
            // Check for listener beans and register them.
            registerListeners();

            // 实例化所有剩余的(非lazy-init初始化)单例
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // 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
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

首先分析prepareRefresh()方法,主要作用是初始化一些参数,内容如下:

AbstractPropertyResolverprotected void prepareRefresh() {
    // Switch to active.
    this.startupDate = System.currentTimeMillis();
    // 将AbstractApplicationContext及其子类设置为活跃状态,也就是没有关闭
    this.closed.set(false);
    // 将AbstractApplicationContext及其子类设置为活跃状态,也就是活跃状态
    this.active.set(true);
    // 判断是否是debug状态,如果是打印debug日志,否则打印追踪日志
    if (logger.isDebugEnabled()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Refreshing " + this);
        }
        else {
            logger.debug("Refreshing " + getDisplayName());
        }
    }

    // 一个用于扩展的方法,主要调用的是其子类的实现,以下几个类都对其进行了实现:
  // AbstractRefreshableWebApplicationContext
  // GenericWebApplicationContext
  // StaticWebApplicationContext
  // 主要作用我们前面说过PropertySource的作用是保存环境变量,配置文件属性,命令行等,
  // 所以通过实现这个方法我们可以在Spring框架启动前对这些属性进行干预和操作,不理解的
  // 可以回头看看:MutablePropertySources 中的propertySourceList字段
    initPropertySources();

    // 这个方法主要看:AbstractPropertyResolver.validateRequiredProperties
    // 主要用于判断某些必须的配置文件属性,命令行,环境变量是否存在,
    getEnvironment().validateRequiredProperties();

    // 存储预刷新 ApplicationListeners...
    // 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<>();
}

上面注释比较清楚的部分我就不赘述了,继续分析一下:validateRequiredProperties方法,这个方法实际调用的是AbstractPropertyResolver中的实现,内容如下:

@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;
    }
}

要了解这个方法需要先解析this.getProperty(key)方法,我跑了一下代码具体执行的是PropertySourcesPropertyResolver类中的getProperty方法,具体内容如下:

@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
  // this.propertySources 实际使用的类是MutablePropertySources,而我们之前讲过MutablePropertySources中存在一个属性propertySourceList
  // 这个list保存了所有的配置,命令行,环境变量等信息,是通过PropertySource对象进行保存的,而propertySource.getProperty(key);
    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 string) {
                    value = resolveNestedPlaceholders(string);
                }
                logKeyFound(key, propertySource, value);
                return convertValueIfNecessary(value, targetValueType);
            }
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Could not find key '" + key + "' in any property source");
    }
    return null;
}

this.propertySources 实际使用的类是MutablePropertySources,而我们之前讲过MutablePropertySources中存在一个属性propertySourceList,这个list保存了所有的配置,命令行,环境变量等信息,是通过PropertySource对象进行保存的,我们再来回忆一下之前的代码:

public class MutablePropertySources implements PropertySources {

    /**
     * 保存配置文件,环境变量,命令行参数等信息的列表集合
     */
    private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
  // 之后的部分省略...................
}

而PropertySource 有一个实现类MapPropertySource,它继承了EnumerablePropertySource 内容如下:

EnumerablePropertySource<Map<String, Object>>public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
  // 内容省略...................
      @Override
      @Nullable
      public Object getProperty(String name) {
          return this.source.get(name);
      }
  // 内容省略....................
}

我们可以看到getProperty方法是通过获取父类的source字段来获取指定属性的值的,那source属性从哪里来呢?可以看下面的代码:

public abstract class PropertySource<T> {

    protected final Log logger = LogFactory.getLog(getClass());

    protected final String name;

    protected final T source;
// 内容省略....................
}

我们可以看粗PropertySource对象中有一个泛型,而通过EnumerablePropertySource<Map<String, Object>>指定了这个泛型为Map<String, Object>从而通过source字段保存了所有的属性集合,看到这里我们就应该明白validateRequiredProperties方法是为了验证requiredProperties列表中指定配置属性,命令行,环境变量等是否存在,如果不存在则抛出异常

@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;
    }
}

具体如何使用呢,我们回到之前最初的refresh方法,这个方法在prepareRefresh方法中调用了validateRequiredProperties方法,所以如果要验证成功必须在prepareRefresh方法调用之前设置requiredProperties的值,我有一个猜想,如果在Springboot 当中要校验某个属性是否已经配置,比如:jdbc 是否已经配置,可以在Springboot 启动之前对requiredProperties进行赋值,具体代码如下所示:

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(Application.class);
    app.addInitializers(applicationContext -> {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        environment.setRequiredProperties("jdbc.config");// 这里只是一个示例,不要纠结配置路径是否正确,明白就好
    });
    app.run(args);
}

然后我启动Springboot 会报错:

The following properties were declared as required but could not be resolved: [jdbc.config]

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值