上面一篇文章提到,在Servlet初始化的時候,擷取屬性後調用initServletBean,這個方法會initWebApplicationContext,這是DispatcherServlet對後面的處理做了很多的預先準備工作,我們一起來看看它到底做了什麼事情。
流程
- 先上代碼,初始化和釋出WebApplicationContext到servlet中,對于真正的建立操作是交給子類的createWebApplicationContext來實作的。
/**
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
// 擷取WebApplicationContext,但是首次取得的值一般為null
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
// 在目前的servlet中,沒有對應的context執行個體時候,建立一個。
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
- 首先擷取WebApplicationContext,一般擷取的值為null
- 然後試着findWebApplicationContext(),這一步主要看其父類的是否已經注冊了對應的context. 父類的context首次也為null
- 如果WebApplicationContext還未找到,那麼嘗試建立一個WebApplicationContext

image.png
-
上一步對于最初的操作,獲得的WebapplicationContext都是null的,是以需要建立一個WebapplicationContext.
為目前的Servlet執行個體化WebApplicationContext
/**
* Instantiate the WebApplicationContext for this servlet, either a default
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
* or a {@link #setContextClass custom context class}, if set.
* <p>This implementation expects custom contexts to implement the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface. Can be overridden in subclasses.
* <p>Do not forget to register this servlet instance as application listener on the
* created context (for triggering its {@link #onRefresh callback}, and to call
* {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
* before returning the context instance.
* @param parent the parent ApplicationContext to use, or {@code null} if none
* @return the WebApplicationContext for this servlet
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//擷取預設的getContextClass,預設的contextClass為XmlWebApplicationContext.class,可以從源碼中看到
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 使用BeanUtils執行個體化ApplicationContext類。
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
- 首先擷取預設的要執行個體化的ApplicationContxt類,預設的為XmlWebApplicationContext
- 使用BeanUtil執行個體化ApplicationContxt的類,BeanUtils是個很有用的方法,可以多看下
- ApplicationContxt被執行個體化之後,做一些配置,設定目前的Environment,設定父類(如果有的話),擷取contextLocation的位置(web.xml中可以設定,當然也有預設值)
- 配置然後重新整理WebApplicationContex類
- 對WebApplicationContext類做配置和重新整理。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
- 首先生成WebApplicationContext的id,用于後面加載Spring-MVC的配置檔案
- 在WebApplicationContext中設定容器的ServletContext,ServiletConfig,并設定預設的命名空間
- 添加ApplicationContext的監聽器,監聽ContextRefresh事件
- 初始化環境屬性
- postProcessWebApplicationContext,初始化WebApplicationContext之後做的事情,預設為空,後面自行擴充的時候可以覆寫
- applyInitializers, 後面可以多了解下,初始器
- 重新整理WebApplicationContext.
- WebApplicationContext的重新整理操作。
- 使用synchronized保證線程安全
- 下面每一個方法都包含一些具體的操作
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 準備重新整理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 告訴子類重新整理内部的bean factory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 準備beanfactory來使用
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// 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();
}
}
}
- 仔細看一下refresh中的每一個大方法,首先prepareRefresh,可以看到prepareRefresh,确實隻做一些準備性的工作,沒有實質性的處理。
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
- obtainFreshBeanFactory()告訴子類重新整理内部的beanfactory, 預設的beanFactory是DefaultListableBeanFactory。
obtainFreshBeanFactory中重新整理BeanFactory的操作是交給其子類來實作的。AbstractRefreshableApplicationContext是真正做了重新整理的操作,并且加載了beanDefinitions.
關于如何loadBeandefenition,後面可以再深入去看
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加載bean的定義
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
- 讓beanFactory準備好使用context. 可以看到beanFactory做了很多的配置
- 設定ClasLoader,SPEL,PropertyEditorRegistrar
- 添加ApplicationContextAware,用于讓bean設定ApplicationContextde .等等
- 解析依賴關系ResolvableDependency相關的内容
- 一些需要早期的bean處理
- 注冊一些系統預設的bean,如Environment,SystemProperties等等
/**
* Configure the factory's standard context characteristics,
* such as the context's ClassLoader and post-processors.
* @param beanFactory the BeanFactory to configure
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
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 interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
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處理之後的處理,這一步可以了解為上一步的後續處理,和上一步類似都是對beanFactory的處理
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
- 激發上下文中建立bean的後續操作,這個内部包含了很多内容,大部分的操作在PostProcessorRegistrationDelegate類裡面的invokeBeanFactoryPostProcessors中,一個方法寫了100多行,内容很多,這裡就粘貼出來了,東西太多。記得PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
包括下一步的registerBeanPostProcessors操作也是由PostProcessorRegistrationDelegate來完成的。這個類看來也挺重要,不過它的通路權限是預設的,看來Spring不想暴露出來給我們用。
PostProcessorRegistrationDelegate
- initMessageSource,操作,進入裡面看到這些操作都是圍繞着BeanFactory在進行,beanFactory設定DelegatingMessageSource。
DispatcherServlet請求流程解析-initWebApplicationContext(二)
- 設定Application廣播發出器,可以看到内部仍然是對beanFactory設定一個預設的類
DispatcherServlet請求流程解析-initWebApplicationContext(二)
- onRefresh方法其實作類基本上都是在初始化主題,GenericWebApplicationContext與AbstractRefreshableWebApplicationContext都是如此
DispatcherServlet請求流程解析-initWebApplicationContext(二)
- 注冊監聽器,看截圖可以看出其主要在上面的事件caster中添加應用事件的bean
- 看一下最後的操作吧,結束重新整理操作,釋出已經重新整理後的事件。
/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
- 重置Spring中的cache
/**
* Reset Spring's common core caches, in particular the {@link ReflectionUtils},
* {@link ResolvableType} and {@link CachedIntrospectionResults} caches.
* @since 4.2
* @see ReflectionUtils#clearCache()
* @see ResolvableType#clearCache()
* @see CachedIntrospectionResults#clearClassLoader(ClassLoader)
*/
protected void resetCommonCaches() {
ReflectionUtils.clearCache();
ResolvableType.clearCache();
CachedIntrospectionResults.clearClassLoader(getClassLoader());
}
回顧
感覺Spring-mvc中的initWebApplicationContext做了很多的事情,先抓住大方向,細節很多,回顧一下這次看到了那些内容。
從initServletBean開始,然後initWebApplicationContext,初始情況下ApplicationContext是為null的,我們需要建立預設的ApplicationContext,Spring-mvc中預設的ApplicationContext是XmlWebApplicationContext。
使用反射建立XmlWebApplicationContext之後,将XmlWebApplicationContext作為參數傳遞到配置和重新整理XmlWebApplicationContext的方法中,接下來的操作,集中在對XmlWebApplicationContext的配置中。
配置Enviriment,ServletConfig,初始屬性,命名空間,過濾器等等。最後調用XmlWebApplicationContext的refresh操作。
refresh操作中主要圍繞bean相關的内容在操作,建立beanFactory,然後對beanFactory做配置,同時加載bean,bean的生命周期等等操作。然後注冊bean以外的内容,配置主題,事件,消息等等,最後結束重新整理,完成整個bean的生命周期。
最後
這裡隻是initWebApplicationContext,感覺bean的生命周期處理也是在這一步内部配置的,後面有關bean的生命周期,可以在内部仔細看一下。
initWebApplicationContext中,涉及到很多的類,很多預設的類,多讀幾遍,也許将來自己造輪子的某一天可以參考Spring的設計。
本人才疏學淺,閱讀Spring源碼的過程,做了一些記錄,供參考作用,如果感興趣,想後續交流歡迎一起探讨。