DispatcherServlet要是用过springmvc应该都知道这个的重要性,在我们的web.xml文件中会配置这个Servlet相当于我们springmvc中央处理器。
我们这里详细分析DispatcherServlet的源码,DispatcherServlet的类结构图如下:
这里有三个接口比较重要:EnvironmentCapable,EcvironmentAware和ApplicationContextAware。在spring中XXXAware在spring里面表示对XXX可以感知,通俗一点就是:如果在某个类里面想要使用spring的一些东西,就可以通过实现XXXAware接口告诉spring,spring看到后就会给你送过来,而接收的方式是通过实现接口唯一的setXXX。比如有一个类想要使用当前的ApplicationContext,那么我们只需要让它实现ApplicationContextAware接口,然后实现接口中唯一的方法void setApplicationContext(ApplicationContext applicationContext),spring会自动传入进来。而EnvironmentCapable恰好,他是用来提供环境的Environment getEnvironment();
什么是环境呢?在我们Environment中封装了ServletContext,同时还封装了ServletConfig,JndiProperty,系统环境变量和系统属性。
看完我们的整体结构接下来就是具体的三个类的处理过程。
对一个Servlet进行分析我们需要首先对init无参方法进行分析下面是代码
public final void init() throws ServletException {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'");
}
try {
//获取配置的初始化值
HttpServletBean.ServletConfigPropertyValues ex = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
//模板方法,可以在子类调用,做一些初始化工作
this.initBeanWrapper(bw);
//将配置的初始化值(如contextConfigLocation)设置到DispatcherServlet
bw.setPropertyValues(ex, true);
} catch (BeansException var4) {
this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4);
throw var4;
}
//模板方法
this.initServletBean();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet \'" + this.getServletName() + "\' configured successfully");
}
}
在HttpServletBean的Init中,首先将Servlet中配置的参数使用BeanWrapper(用来操作JavaBean的一个工具),设置到DispathcerServlet的相关属性,然后调用模板方法InitServletBean,子类就通过这个方法初始化。
通过上面的分析可以知道,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");
}
}
在initServletBean()中只有两个作用一个是初始化webApplicationContext一个是初始化FrameworkServlet。
initWebApplicationContext()方法有:
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) {
//通过contextAttribute参数获取
wac = this.findWebApplicationContext();
}
if(wac == null) {
//手动创建一个webApplicationContext
wac = this.createWebApplicationContext((WebApplicationContext)rootContext);
}
if(!this.refreshEventReceived) {
//如果没刷新就刷新
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中。
获取根容器的原理是将自己容器
sc.getAttribute(“org.springframework.web.context.WebApplicationContext.ROOT”);
设置webApplicationContext有三种方法。
第一种方法是在构造方法中已经传递webAppliicationContext参数,这时只需要对其进行一些设置。在Servlet3.0以后的环境中,Servlet3.0之后可以在程序中使用ServletContext.addServlet方式注册Servlet,这时就可以在新建FrameworkServlet和其子类的时候通过构成方法传递已经准备好的webApplicationContext。
第二种方式是webApplicationContext已经在ServletContext中了。这时只需要在配置Servlet的时候将ServketContext中的webApplicationContext的name配置到contextAttribute属性就可以了。
<init-param>
<param-name>contextAttribute</param-name>
<param-value>1</param-value>
</init-param>
第三种方法是在前面两种方式都无效的情况下自己创建一个。正常情况下就是使用的这种方式。创建过程在createWebApplicationContext方法中。
这里要说明一下什么是WebApplicationContext。从名字上面来看他是web应用上下文容器 其中包含了我们spring-MVC配置文件中所有配置的东西初始化。
onRefresh是DispatcherServlet的入口方法。onRefresh中简单地调用了initStrategies中调用了9个初始化方法。
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
具体是初始化9个组件。
分析以LocaleResolver为例
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = (LocaleResolver)context.getBean("localeResolver", LocaleResolver.class);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
}
} catch (NoSuchBeanDefinitionException var3) {
this.localeResolver = (LocaleResolver)this.getDefaultStrategy(context, LocaleResolver.class);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Unable to locate LocaleResolver with name \'localeResolver\': using default [" + this.localeResolver + "]");
}
}
}
在初始化方式中分两步:首先通过context.getBean在容器里面按注册时的名称或类型进行查找,所以在Spring MVC的配置文件中需要配置相应类型的组件,容器就可以自动找到。如果找不到就调用默认组件,这里的context指的是web应用上下文。这里的默认组件只会有8个没有文件上传,因为并不是所有都需要默认上传。