Spring MVC笔记(二) -- 启动

servlet相关知识可参考:Java Web(一) Servlet详解!!

一、ContextLoaderListener

Spring MVC的web.xml部分配置如下:


<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

servlet容器在启动的使用会调用ServletContextListener接口的contextInitialized方法。ContextLoaderListener就实现了该接口
ContextLoaderListener

看ContextLoaderListener类的contextInitialized

public void contextInitialized(ServletContextEvent event) {
	initWebApplicationContext(event.getServletContext());
}

initWebApplicationContext方法在父类ContextLoader中

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
		//不能重复设置容器
	}
	...
	long startTime = System.currentTimeMillis();
	try {
		if (this.context == null) {
			//创建容器
			this.context = createWebApplicationContext(servletContext);
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				...
			//刷新容器
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
		...
		//设置容器
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
		...

		return this.context;
	}
	catch...
}

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	//默认返回XmlWebApplicationContext
	Class<?> contextClass = determineContextClass(sc);
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		//抛异常
	}
	//反射生成实例
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

ContextLoaderListener想做的事情是创建一个IOC容器,首先会先检查servletContext是否已包含IOC容器,没有就新创建一个,默认会读取ContextLoader.properties配置文件,得到是XmlWebApplicationContext的实例容器。
ContextLoader.properties内容如下:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

容器创建完成后就开始初始化刷新容器

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		//获取contextId属性
		String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
		if (idParam != null) {
			wac.setId(idParam);
		}
		else {
			//使用默认的ID
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}

	wac.setServletContext(sc);
	//获取配置文件路径,对应属性contextConfigLocation,即上面的classpath*:applicationContext.xml
	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
	if (configLocationParam != null) {
		//设置配置文件
		wac.setConfigLocation(configLocationParam);
	}
	...
	//这里开始读取配置文件,初始化bean等等
	wac.refresh();
}

二、DispatcherServlet

<servlet>
     <servlet-name>season</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <!--Spring MVC容器-->
     <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath*:spring-servlet.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
 </servlet>

servlet的生命周期由servlet的容器来控制,分3个阶段:初始化、运行、销毁。初始化会调用servlet的init(ServletConfig config)方法
DispatcherServlet

DispatcherServlet实现了Servlet接口,其init方法在父类HttpServletBean中

public final void init() throws ServletException {
	//获取并验证属性
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			//注册自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			//留给子类实现
			initBeanWrapper(bw);
			//属性注入,例如contextConfigLocation属性
			bw.setPropertyValues(pvs, true);
		}
		catch...
	}
	//留给子类扩展
	initServletBean();
}

FrameworkServlet类

protected final void initServletBean() throws ServletException {
	...
	long startTime = System.currentTimeMillis();
	try {
		//初始化容器
		this.webApplicationContext = initWebApplicationContext();
		//留给子类实现
		initFrameworkServlet();
	}
	catch...
}

protected WebApplicationContext initWebApplicationContext() {
	//拿到在ContextLoaderListener那里实例化的容器
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;
	if (this.webApplicationContext != null) {
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				if (cwac.getParent() == null) {
					cwac.setParent(rootContext);
				}
			//刷新容器(解析配置文件,bean初始化等)
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {
		//查找容器
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		//创建容器
		wac = createWebApplicationContext(rootContext);
	}
	if (!this.refreshEventReceived) {
		//刷新容器(解析配置文件,bean初始化等)
		onRefresh(wac);
	}
	if (this.publishContext) {
		// 保存到ServletContext
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}
	return wac;
}

FrameworkServlet类覆盖了HttpServletBean中的initServletBean()方法,来完成Spring MVC容器的初始化,并把容器的父容器设置为ContextLoaderListener实例化的容器。

创建容器方法如下:

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
	return createWebApplicationContext((ApplicationContext) parent);
}

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
	//默认是XmlWebApplicationContext类
	Class<?> contextClass = getContextClass();
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw...
	}
	ConfigurableWebApplicationContext wac =
			(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	wac.setEnvironment(getEnvironment());
	wac.setParent(parent);
	//拿到contextConfigLocation属性
	String configLocation = getContextConfigLocation();
	if (configLocation != null) {
		wac.setConfigLocation(configLocation);
	}
	//刷新容器
	configureAndRefreshWebApplicationContext(wac);
	return wac;
}

SpringMVC会存在两个容器,一个是在ContextLoaderListener那实例化,一个在DispatcherServlet实例化

DispatcherServlet实例化容器的时候会调用configureAndRefreshWebApplicationContext方法

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		if (this.contextId != null) {
			wac.setId(this.contextId);
		}
		else {
			// 默认id
				(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()));
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
	}

	postProcessWebApplicationContext(wac);
	applyInitializers(wac);
	//加载配置文件及整合parent到wac
	wac.refresh();
}

其中添加的刷新的监听事件如下

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

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

public void onApplicationEvent(ContextRefreshedEvent event) {
	this.refreshEventReceived = true;
	onRefresh(event.getApplicationContext());
}

其中DispatcherServlet重写了onRefresh方法

protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
	//初始化MultipartResolver(文件处理)
	initMultipartResolver(context);
	//初始化LocaleResolver(语言处理)
	initLocaleResolver(context);
	//初始化ThemeResolver(主题处理)
	initThemeResolver(context);
	//初始化HandlerMappings
	initHandlerMappings(context);
	//初始化HandlerAdapters
	initHandlerAdapters(context);
	//初始化HandlerExceptionResolvers(异常处理)
	initHandlerExceptionResolvers(context);
	//初始化RequestToViewNameTranslator
	initRequestToViewNameTranslator(context);
	//初始化ViewResolvers(视图解析)
	initViewResolvers(context);
	//初始化FlashMapManager(重定向,参数处理)
	initFlashMapManager(context);
}

DispatcherServlet会在容器加载完配置文件和bean之后,在刷新的时候初始化好各个组件

猜你喜欢

转载自blog.csdn.net/seasonLai/article/details/82791436