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

待续...

posted @ 2020-07-26 11:06  飘飘来来荡荡去去  阅读(599)  评论(0编辑  收藏  举报