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类的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实现了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之后,在刷新的时候初始化好各个组件