SpringBoot启动分析5:刷新环境
1.1 刷新流程概览
跟进源码,大概看下源码对于环境刷新的整体步骤:
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
}
从上方可以看出最终调用了父类AbstractApplicationContext的refresh()方法。
1.2 刷新流程分析
1.2.1 准备刷新
跟进源码,如下所示:
prepareRefresh();
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
@Override
protected void prepareRefresh() {
this.scanner.clearCache();
super.prepareRefresh();
}
}
1.2.1.1 清除缓存
还记得scanner是当时创建上下文环境对象AnnotationConfigServletWebServerApplicationContext时所初始化的,当时对该类的初始化是这样的:
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
// 当时通过setEnvironment进行初始化,实际是调用父类ClassPathScanningCandidateComponentProvider进行初始化
this.scanner.setEnvironment(environment);
}
}
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
// 实际初始化方法
public void setEnvironment(Environment environment) {
Assert.notNull(environment, "Environment must not be null");
this.environment = environment;
this.conditionEvaluator = null;
}
}
ClassPathBeanDefinitionScanner是类扫描器,也就是它的作用就是将指定包下的类通过一定规则过滤后将Class信息包装成BeanDefinition的形式注册到IOC容器中,在SpringBoot工程中它扫描的是主程序类下的包和所有子包。而ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner的基类,其本身主要作用是包扫描,ClassPathBeanDefinitionScanner在其基础上做了注册功能,所以ClassPathBeanDefinitionScanner需要传入一个BeanDefinitionRegistry对象,而ClassPathScanningCandidateComponentProvider扫描的对象是并不需要注册到BeanDefinitionRegistry中去的。
这里的clearCache方法,主要是用于清理本地的元数据缓存,以下跟进源码:
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
public void clearCache() {
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
public void clearCache() {
if (this.metadataReaderCache instanceof LocalResourceCache) {
synchronized (this.metadataReaderCache) {
this.metadataReaderCache.clear();
}
}
else if (this.metadataReaderCache != null) {
setCacheLimit(DEFAULT_CACHE_LIMIT);
}
}
public void setCacheLimit(int cacheLimit) {
if (cacheLimit <= 0) {
this.metadataReaderCache = null;
}
else if (this.metadataReaderCache instanceof LocalResourceCache) {
((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);
}
else {
// 最终实例化LocalResourceCache并设置本地MetadataReader缓存的默认最大条目数为256
this.metadataReaderCache = new LocalResourceCache(cacheLimit);
}
}
}
1.2.1.2 准备刷新
这里调用了父类AbstractApplicationContext的prepareRefresh()方法,跟进源码:
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
protected void prepareRefresh() {
// 设置启动时间
this.startupDate = System.currentTimeMillis();
// 设置关闭状态为false
this.closed.set(false);
// 设置激活状态为true
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
initPropertySources();
getEnvironment().validateRequiredProperties();
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.earlyApplicationEvents = new LinkedHashSet<>();
}
}
初始化属性源
跟进initPropertySources()方法源码:
public class GenericWebApplicationContext extends GenericApplicationContext implements ConfigurableWebApplicationContext, ThemeSource {
@Override
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
}
}
}
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
}
public abstract class WebApplicationContextUtils {
public static void initServletPropertySources(MutablePropertySources sources,@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
Assert.notNull(sources, "'propertySources' must not be null");
String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}
}
这里主要是替换属性源,在应用启动之前替换一些属性占位符,但是在启动时由于servletContext、servletConfig都为空,所以这里并没有实现。
校验必备属性
跟进getEnvironment().validateRequiredProperties()源码:
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
@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;
}
}
}
若存在必备属性没有配置则抛出异常,我们可以在初始化器中获取环境对象,并在环境对象中设置必备属性,改造之前的初始化器:
@Order(1)
public class Order1Initializer implements ApplicationContextInitializer {
private Logger log = LoggerFactory.getLogger(getClass());
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
environment.setRequiredProperties("springboot-start-properties");
}
}
启动后会抛出如下异常:
2020-08-16 11:02:37.146 WARN 7052 --- [ main] o.s.boot.SpringApplication : Error handling failed (ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@223f3642, started on Sun Aug 16 11:02:37 CST 2020)
2020-08-16 11:02:37.150 ERROR 7052 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [springboot-start-properties]
我们可以通过在全局配置文件中添加必备属性配置解决,例如:
springboot-start-properties=xxxxxxxxxxxx
初始化本地监听器
只是将之前有初始化过的监听器赋值给本地监听器变量,为了后期刷新使用,同时也初始化好对应的监听事件列表:
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}else {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.earlyApplicationEvents = new LinkedHashSet<>();
1.2.2 刷新BeanFactory
跟进源码:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
}
public void setSerializationId(@Nullable String serializationId) {
if (serializationId != null) {
serializableFactories.put(serializationId, new WeakReference<>(this));
}
else if (this.serializationId != null) {
serializableFactories.remove(this.serializationId);
}
this.serializationId = serializationId;
}
这里仅仅是为beanFactory设置一个id默认id值为application,并且将ID和beanFactory放入到一个map类型的工厂实例中。
1.2.3 配置BeanFactory
跟进源码如下:
prepareBeanFactory(beanFactory);
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
String ENVIRONMENT_BEAN_NAME = "environment";
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
}
这里主要设置了beanFactory的一些属性、设置忽略自动装配接口、添加后置处理器以及注册一些组件。
1.2.4 后处理BeanFactory
跟进源码:
postProcessBeanFactory(beanFactory);
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
}
待续...