接下来解析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]
1650

被折叠的 条评论
为什么被折叠?



