继承实现关系:
public class DispatcherServlet extends FrameworkServlet
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
public abstract class HttpServlet extends GenericServlet
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable
public interface Servlet
从HttpServletBean中可知,FrameworkServlet的初始化方法应该是initServletBean
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet \'" + this.getServletName() + "\'");
if(this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization started");
}
long startTime = System.currentTimeMillis();
try {
初始化WebApplicationContext
this.webApplicationContext = this.initWebApplicationContext();
初始化FrameworkServlet
this.initFrameworkServlet();
} catch (ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
} catch (RuntimeException var6) {
this.logger.error("Context initialization failed", var6);
throw var6;
}
if(this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization completed in " + elapsedTime + " ms");
}
}
做了俩件事:初始化WebApplicationContext
初始化FrameworkServlet,是一个模板方法,子类可以覆盖然后在里面做一些初始化工作,但子类并没有使用它。
所以FrameworkServlet在构建过程中主要作用就是初始化了WebApplicationContext
初始化WebApplicationContext的方法如下:
protected WebApplicationContext initWebApplicationContext() {
获取rootContext
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
如果已经通过构建方法设置了webApplicationContext
if(this.webApplicationContext != null) {
wac = this.webApplicationContext;
if(wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
if(!attrName.isActive()) {
if(attrName.getParent() == null) {
attrName.setParent(rootContext);
}
this.configureAndRefreshWebApplicationContext(attrName);
}
}
}
if(wac == null) {
当webApplicationContext已经存在ServletContext中时,通过配置在Servlet中的contextAttribute参数获取
wac = this.findWebApplicationContext();
}
if(wac == null) {
如果WebApplicationContex还没有创建,则创建一个
wac = this.createWebApplicationContext(rootContext);
}
if(!this.refreshEventReceived) {
当ContextRefreshedEvent事件没有触发时调用此方法,模板方法,可以在子类中重写。
this.onRefresh(wac);
}
if(this.publishContext) {
将ApplicationContext保存到ServletContext中
String attrName1 = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName1, wac);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
}
}
return wac;
}
initWebApplicationContext做了三件事:
1、获取spring根容器rootContext
2、设置webApplicationContext并根据情况调用onRefresh方法
3、将webApplicationContext设置到ServletContext中
org.springframework.web.context.WebApplicationContext
下面详细介绍这三件事:
1、获取spring根容器rootContext
获取根容器的原理:
默认情况下spring会将自己的容器设置成ServletContext属性,默认根容器的Key为org.springframework.web.context.WebApplicationContext.ROOT
实现类在:org.springframework.web.context.WebApplicationContext
public interface WebApplicationContext extends ApplicationContext {
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
String SCOPE_REQUEST = "request";
String SCOPE_SESSION = "session";
String SCOPE_GLOBAL_SESSION = "globalSession";
String SCOPE_APPLICATION = "application";
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
ServletContext getServletContext();
}
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
所以获取根容器只需要调用ServletContext的getAttribute就可以了
ServletContext#getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");
2、设置webApplicationContext并根据情况调用onRefresh方法设置webApplication一共有三种办法:
一、
第一种方法是根据构造方法传递webApplicationContext参数,这时只需要对其进行一些设置即可。这种方法主要用于servlet3.0以后的环境中,servlet3.0之后可以在程序中
使用ServletContext.addServlet方式注册Servlet,这时就可以在新建FrameworkServlet和其子类的时候,通过构造方法传递webApplicationContext
二、
第二种方法是webApplicationContext已经在servletContext中了,这时只需要在配置Servlet的时候将ServletContext中的webApplicationContext的
name配置到contextAttribute属性就可以了。比如,在ServletContext中有一个叫study的webApplicationContext,可以这么将它配置到SpringMVC中:
<servlet>
<servlet-name>selfstudy</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/*.xml</param-value>
<param-name>contextAttribute</param-name>
<param-value>study</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
三、第三种方法是在前俩种方法都无效的情况下自己建立一个。一般情况下就是使用的这种方式。创建过程在createWebApplicationContext方法中,
createWebApplicationContext内部又调用了configureAndRefreshWebApplicationContext方法,代码如下:
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
return this.createWebApplicationContext((ApplicationContext)parent);
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
获取创建类型
Class contextClass = this.getContextClass();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name \'" + this.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 \'" + this.getServletName() + "\': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
具体创建
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent);
将设置的contextConfigLocation参数传给wac,默认传入WEB/INFO/[ServletName]-Servlet.xml
wac.setConfigLocation(this.getContextConfigLocation());
调用方法 配置和刷新上下文方法
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
这个contextID仍然设置它原本默认的id
基于可用的信息给它分配一个更有用的id
if(this.contextId != null) {
wac.setId(this.contextId);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + "/" + this.getServletName());
}
}
wac.setServletContext(this.getServletContext());
wac.setServletConfig(this.getServletConfig());
wac.setNamespace(this.getNamespace());
添加监听ContextRefreshEvent监听器
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null)));
ConfigurableEnvironment env = wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
}
this.postProcessWebApplicationContext(wac);
this.applyInitializers(wac);
wac.refresh();
}
做的事情:
1、首先调用getContextClass获取到要创建的类型,它可以通过contextClass属性设置到Servlet中默认使用
org.springframework.web.context.support.XmlWebApplicationContext
2、然后检查是否属于ConfigurableWebApplicationContext类型,如果不属于,就跑出异常。
3、接着通过BeanUtils.instantiateClass(contextClass)进行创建,创建后将设置的contextConfigLocation传入,如果没有设置,默认传入WEB-INFO/[ServletName]-Servlet.xml,最后进行配置。
事情1的实现类以及说明:
org.springframework.web.context.support.XmlWebApplicationContext
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
public XmlWebApplicationContext() {
}
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = this.getConfigLocations();
if(configLocations != null) {
String[] var3 = configLocations;
int var4 = configLocations.length;
for(int var5 = 0; var5 < var4; ++var5) {
String configLocation = var3[var5];
reader.loadBeanDefinitions(configLocation);
}
}
}
protected String[] getDefaultConfigLocations() {
return this.getNamespace() != null?new String[]{"/WEB-INF/" + this.getNamespace() + ".xml"}:new String[]{"/WEB-INF/applicationContext.xml"};
}
}
接上面3,说一下配置的事情。在configureAndRefreshWebApplicationContext方法中给wac添加了监听器
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null)));
SourceFilteringListener可以根据输入的参数进行选择,所以实际监听的是ContextRefreshListener所监听的事件。
ContextRefreshListener是FrameworkServlet的内部类,监听ContextRefreshedEvent事件,当接收到消息时调用
FrameworkServlet的onApplicationEvent方法,在onApplicationEvent中会调用一次onRefresh方法,并将
refreshEventReceived(接收到刷新事件)标志设置为true,表示已经refresh过了。代码如下:
实现类:
org.springframework.web.servlet.FrameworkServlet
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
private ContextRefreshListener() {
}
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
this.onRefresh(event.getApplicationContext());
}
在initWebApplicationContext方法中,后面会根据refreshEventReceived标志来判断是否要运行onRefresh。
if(!this.refreshEventReceived) {
当ContextRefreshedEvent事件没有触发时调用此方法,模板方法,可以在子类中重写。
this.onRefresh(wac);
}
当使用第三种方法初始化时已经refresh,不需要再次调用onRefresh。同样在第一种方式中也调用了configureAndRefreshWebApplication方法,也refresh过,所以只有第二种方式初始化webApplicationContext的时候才会在这里调用onRefresh方法。不过不管用哪种方式调用,onRefresh
最终肯会而且只会调用一次,并且DispatcherServlet正是通过重写这个模板方法来实现初始化的。
3、将webApplicationContext设置到ServletContext中
最后会根据publishContext标志判断是否将创建出来的webApplicationContext设置到ServletContxt的属性中,publicContext标志可以在配置
Servlet时通过init-param参数进行设置,HttpServletBean初始化时会将其设置到publicContext参数。之所以将创建出来的webApplicatonContext
设置到ServletContext的属性中,主要是为了方便获取。
配置Servlet时可以设置一些初始化参数,总结:
contextAttribute:在ServletContext属性中,要用作WebApplicationContext的属性名称。
contextClass: 创建WebApplicationContext的类型
contextConfigLocation:SpringMVC配置文件的位置
publishContext:是否将webApplicationContext设置到ServletContext的属性。