DispatcherServlet和Spring应用上下文初始化详解

SpringMVC中的Servlet一共有三层:HttpServletBean、FrameworkServlet和DispatcherServlet。

HttpServletBean直接继承于Java的HttpServlet,其作用是将Servlet中配置的参数设置到相应的属性。该类实现Servlet生命周期中的init方法,但并未实现destroy方法。

FrameworkServlet初始化了WebApplicationContext。并且实现了Servlet生命周期中的destroy方法,在该方法中调用了Servlet中的IoC容器的close方法。

DispatcherServlet初始化了自身的9个组件,并在运行期负责请求的分发。

HttpServletBean

HttpServletBean是SpringWeb模块中基于Servlet最基础的Bean。其继承于HttpServlet。

在其实现的init方法中,完成SpringWeb模块中IoC容器的初始化,该方法是一个抽象方法,具体的实现是由FrameworkServlet完成的。

public final void init() throws ServletException {
	if (logger.isDebugEnabled()) {
		logger.debug("Initializing servlet '" + getServletName() + "'");
	}

	//  根据初始化参数来设置Bean属性
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			// 根据HttpServletBean来创建BeanWrapper 
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			// 创建资源加载器
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			// 注册属性编辑器
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			// 设置Bean属性元数据
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}
	}

	// 该方法在HttpServletBean中并未实现,而是由FrameworkServlet实现.
	initServletBean();

	if (logger.isDebugEnabled()) {
		logger.debug("Servlet '" + getServletName() + "' configured successfully");
	}
}
FrameworkServlet

FrameworkServlet最重要的作用便是负责Servlet中的IoC容器的初始化。在其实现的initServletBean方法中,通过调用initWebApplicationContext方法来完成Web容器的初始化工作。

// FrameworkServlet#initWebApplicationContext
protected final void initServletBean() throws ServletException {
	getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
	if (this.logger.isInfoEnabled()) {
		this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
	}
	long startTime = System.currentTimeMillis();

	try {
		// 初始化Web应用上下文
		this.webApplicationContext = initWebApplicationContext();
		// 空方法,留给子类实现,DispatcherServlet并未实现该方法
		initFrameworkServlet();
	} catch (ServletException | RuntimeException ex) {
		this.logger.error("Context initialization failed", ex);
		throw ex;
	}

	if (this.logger.isInfoEnabled()) {
		long elapsedTime = System.currentTimeMillis() - startTime;
		this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
				elapsedTime + " ms");
	}
}

在initWebApplicationContext方法中完成了子容器的创建工作。首先从ServletContext中获取到根容器,获取的key为WebApplicationContext的全限定名加上.ROOT。

protected WebApplicationContext initWebApplicationContext() {
	// 父容器是由ContextLoaderListener来创建的,所以如果未在web.xml中配置,那么将没有父容器,只有一个子容器
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;
	// 第一次执行该方法时 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()) {
				if (cwac.getParent() == null) {
					cwac.setParent(rootContext);
				}
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	// 尝试从ServletContext中获取WebApplicationContext ,通常获取不到
	if (wac == null) {
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// 这里才是真正创建WebApplicationContext 
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		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;
}
// WebApplicationContextUtils#getWebApplicationContext
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
	return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

在createWebApplicationContext方法中,首先通过getContextClass方法来获取要创建的上下文容器类型,如果未指定,默认为XmlWebApplicationContext。

// public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
// private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		// 获取要创建的Web容器类型
		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 + "]");
		}
		// 如果要创建的Web容器类型不是ConfigurableWebApplicationContext类型则抛出异常
		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");
		}
		// 使用反射来实例化Web容器
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		// 设置环境变量
		wac.setEnvironment(getEnvironment());
		// 设置父容器
		wac.setParent(parent);
		String configLocation = getContextConfigLocation();
		if (configLocation != null) {
			// 设置配置文件路径
			wac.setConfigLocation(configLocation);
		}
		// 配置并刷新Web容器
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

在configureAndRefreshWebApplicationContext方法中值的注意的就是手动添加的ApplicationListener-SourceFilteringListener。在创建SourceFilterListener时,传入了一个ContextRefreshListener实例。DispatcherServlet的onRefresh方法就是由该监听器的onApplicationEvent方法调用的。

因为在Spring应用上下文中事件机制中事件具有层次传播性(子类的应用上下文事件可以传播到父应用上下文中),因此如果不做任何措施,那么相同的事件可能被重复消费。如何解决事件层次传播问题,无疑SourceFilteringListener极具有参考价值。

// FrameworkServlet#configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	// 设置Web应用上下文ID
	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());
		}
	}
	// 将ServletContext设置到Web应用上下文中
	wac.setServletContext(getServletContext());
	// 将ServletConfig设置到Web应用上下文中
	wac.setServletConfig(getServletConfig());
	// 将Namespace设置到Web应用上下文中
	wac.setNamespace(getNamespace());
	// 添加ApplicationContextListener到Web应用上下文中
	wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

	// 将ServletContext以及ServletConfig设置到Environment实例中
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
	}
	// 扩展方法,留给子类实现
	postProcessWebApplicationContext(wac);
	// 应用ApplicationContextInitializer
	applyInitializers(wac);
	// 调用大名鼎鼎的refresh方法
	wac.refresh();
}

这里先简单看下SourceFilteringListener的onApplicationEvent方法实现。其处理事件层次性传播很简单,就是通过ApplicationEvent的getSource方法返回的事件源对象(Spring 应用上下文)是否与创建该实例时指定的应用上下文是否相等,如果相对则处理事件,否则不处理。

// SourceFilteringListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
	if (event.getSource() == this.source) {
		onApplicationEventInternal(event);
	}
}
// FrameworkServlet#onApplicationEvent
public void onApplicationEvent(ContextRefreshedEvent event) {
	this.refreshEventReceived = true;
	onRefresh(event.getApplicationContext());
}

// FrameworkServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
	// For subclasses: do nothing by default.
}

在onApplicationEventInternal方法中直接调用了创建SourceFilteringListener时指定的ApplicationListener实例的onApplicationEvent方法,这个实例为ContextRefreshListener(不明白的同学可以看看上面的configureAndRefreshWebApplicationContext方法)。

// SourceFilteringListener#onApplicationEventInternal
protected void onApplicationEventInternal(ApplicationEvent event) {
	if (this.delegate == null) {
		throw new IllegalStateException(
				"Must specify a delegate object or override the onApplicationEventInternal method");
	}
	this.delegate.onApplicationEvent(event);
}

ContextRefreshListener是FrameworkServlet的一个私有内部类,在其实现的onApplicationEvent方法中,直接调用了FrameworkServlet的onApplicationEvent方法。而DispatcherServlet重写了该方法。

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		FrameworkServlet.this.onApplicationEvent(event);
	}
}
DispatcherServlet

DispatcherServlet通过监听容器刷新完毕的事件来完成SpringMVC九大组件的初始化。在其实现父类FramworkServlet的onRefresh方法中调用了initStrategies方法。

// DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
}
// DispatcherServlet#initStrategies
protected void initStrategies(ApplicationContext context) {
	// 初始化多媒体解析器
	initMultipartResolver(context);
	// 初始化本地化解析器
	initLocaleResolver(context);
	// 初始化主题解析器
	initThemeResolver(context);
	// 初始化请求处理器映射器
	initHandlerMappings(context);
	// 初始化请求处理器适配器
	initHandlerAdapters(context);
	// 初始化异常解析器
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	// 初始化视图解析器
	initViewResolvers(context);
	initFlashMapManager(context);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值