SpringMVC源码情操陶冶-DispatcherServlet父类简析
阅读源码有助于陶冶情操,本文对springmvc作个简单的向导
springmvc-web.xml配置
<servlet>
        <servlet-name>dispatch</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--springmvc配置文件加载路径-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/mvc/*.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatch</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
DispatcherServlet类结构
查看源码可以得知类结构如下
DispatcherServlet extends FrameworkServlet
FrameworkServlet extends HttpServletBean implements ApplicationContextAware
HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
本文将对Dispatcher的父类作下向导,方便读者以及博主自我查阅
HttpServletBean-web服务抽象类
具体的介绍可以详看官方注释,这里我们只关注它的init方法,代码如下
//开始初始化springmvc servlet,并实例化相应的PropertyValues供子类调用
public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}
		// Set bean properties from init parameters.
		try {
		//此处便是读取<servlet>节点中的<init-param>参数保存至MutablePropertyValues#propertyValueList集合中
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			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) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}
		// Let subclasses do whatever initialization they like.供子类复写
		initServletBean();
		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}
小结:
从以上代码可见只是简单的获取servlet中的<init-param>参数并绑定到BeanWrapper对象中,并通过initServletBean()方法供子类完善其余的功能。
FrameworkServlet-结合spring的web抽象类
官方简单的解释为:Spring的web基本servlet框架,通过基于javaBean解决方式来集合Spring的application context上下文。
- ApplicationContextAware接口
 该接口的使用主要是为当前类可以拥有spring application context上下文类,可方便的获取bean对象,内部只有一个接口方法setApplicationContext(ApplicationContext app),在FrameworkServlet中的实现
public void setApplicationContext(ApplicationContext applicationContext) {
		//由spring调用并进行相应的applicationContext的注入
		if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
			this.webApplicationContext = (WebApplicationContext) applicationContext;
			this.webApplicationContextInjected = true;
		}
	}
- doService()-抽象接口 
 主要是供子类去实现处理servlet的请求
- 复写 - initServletBean()方法,完善功能
@Override
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 {
			//此处的初始化操作类似于ContextLoader#initWebApplicationContext
			this.webApplicationContext = initWebApplicationContext();
			//供子类调用去初始化另外的功能
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (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");
		}
	}
- 承接initServletBean()方法的initWebApplicationContext(),代码如下
protected WebApplicationContext initWebApplicationContext() {
		//一般来说,spring初始化时间比springmvc要早,所以rootContext一般都存在
		WebApplicationContext rootContext =				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		//先检查spring是否已经注入了webApplicationContext,其中的刷新操作类似于`ContextLoader#initWebApplicationContext`,初次调用此处为空
		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) {
						//springmvc的父类为spring上下文
						cwac.setParent(rootContext);
					}
					//此处方法不同于`ContextLoader`的相同方法
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//再而尝试从ServletContext的`contextAttribute`对应的值去获取
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		//不然则创建新的webApplicationContext
		if (wac == null) {
			wac = createWebApplicationContext(rootContext);
		}
		//springmvc的第一次刷新
		if (!this.refreshEventReceived) {
			//调用子类的onRefresh(wac)方法初始化springmvc
			onRefresh(wac);
		}
		//保存至servletContext属性中
		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;
	}
- FrameworkServlet#createWebApplicationContext()-springmvc创建上下文对象
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		//默认为XmlWebApplicationContext.class也可指定contextClass属性在web.xml
		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 + "]");
		}
		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");
		}
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		//设置springmvc的配置文件即 contextConfigLocation属性
		wac.setConfigLocation(getContextConfigLocation());
		//此处与ContextLoader#configureAndRefreshWebApplicationContext()类似
		configureAndRefreshWebApplicationContext(wac);
		return wac;
	}
- 承接createWebApplicationContext方法的FrameworkServlet#configureAndRefreshWebApplicationContext()-springmvc的刷新处理,不同于spring
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		//设置id属性
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
			}
		}
		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		//默认的namespace为getServletName()+"-servlet"
		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);
		//读取web.xml中的`context-param`节点中的globalInitializerClasses、contextInitializerClasses并初始化
		applyInitializers(wac);
		//刷新,此处可见Spring源码情操陶冶-AbstractApplicationContext博文
		wac.refresh();
	}
小结:
FrameworkServlet实例化springmvc上下文环境,与spring初始化上下文环境类似
onRefresh()方法开放出来供子类DispatherServlet调用完善springmvc的相关初始化操作
读取
init-param为contextConfigLocation属性的值用于加载springmvc的配置文件,其中的bean文件的读取操作可参照Spring源码情操陶冶-AbstractApplicationContext博文
springmvc的namespace为
getServletName()+'-servlet',比如dispatcher-servlet,用于未指定contextConfigLocation属性的话,便会去加载\WEB-INF\${namespace}.xml,比如\WEB-INF\dispatcher-servlet.xml,而对于spring,默认加载的为\WEB-INF\applicationContext.xml
applicationContext.xml和dispatcher-servlet配置文件的加载,既支持classpath方式的加载,也支持WEB-INF方式的加载。后者是通过ServletContextResourceLoader加载的,实质是通过ServletContext.getRealPath()方法加载web环境下的web-inf的资源,其中参数必须以"/"为开头
下节预告
SpringMVC源码情操陶冶-DispatcherServlet类简析
SpringMVC源码情操陶冶-DispatcherServlet父类简析的更多相关文章
- SpringMVC源码情操陶冶-DispatcherServlet类简析(一)
		阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ... 
- SpringMVC源码情操陶冶-HandlerAdapter适配器简析
		springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ... 
- SpringMVC源码情操陶冶-DispatcherServlet
		本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ... 
- SpringMVC源码情操陶冶-DispatcherServlet简析(二)
		承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ... 
- SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器
		承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ... 
- SpringMVC源码情操陶冶-FreeMarker之web配置
		前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ... 
- SpringMVC源码情操陶冶-AbstractHandlerMethodMapping
		承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ... 
- SpringMVC源码情操陶冶-AbstractUrlHandlerMapping
		承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,前文主要讲解了如何获取handler处理对象,本文将针对beanName注册为handler对象作下解析 Abst ... 
- SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器
		mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ... 
随机推荐
- HDU 2079 dp解法
			选课时间(题目已修改,注意读题) Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ... 
- hdu_1045Fire Net(二分图匹配)
			hdu_1045Fire Net(二分图匹配) 标签: 图论 二分图匹配 题目链接 Fire Net Time Limit: 2000/1000 MS (Java/Others) Memory Lim ... 
- 解决 重启nginx: [alert] kill(189, 1) failed (3: No such process)
			解决 nginx: [alert] kill(189, 1) failed (3: No such process) [root@localhost/]# nginx -s reloadnginx: ... 
- JS实现单选按钮回显时页面效果出现,但选中单选框的值为空
			最近做了很多前端页面的工作,遇到的一个感觉很头疼的问题在这里记一下: 经常用JS回显单选框,但是明明从页面效果上来看,单选框已经被选中了,可是却不能触发单选框的change事件,取值的时候用某种方法取 ... 
- [OpenCV学习笔记1][OpenCV基本数据类型]
			CvPoint基于二维整形坐标轴的点typedef struct CvPoint{int x; /* X 坐标, 通常以 0 为基点 */int y; /* y 坐标,通常以 0 为基点 */}CvP ... 
- [国嵌攻略][148][MTD系统架构]
			MTD设备概述 Flash在嵌入式系统中是必不可少的,它是bootloader.Linux内核和文件系统的最佳载体.在Linux内核中引入了MTD子系统为NOR Flash和Nand FLash设备提 ... 
- APP测试时常用adb命令
			ADB全称Android Debug Bridge, 是android sdk里的一个工具, 用这个工具可以直接操作管理android模拟器或者真实的andriod设备(手机),故在其实工作可以给我们 ... 
- IOS中UIScrollView的contentSize、contentOffset和contentInset属性
			IOS中,UIScrollView是可以滚动的视图,其中最常用的UITableView就是继承了UIScrollView. 跟所有的view一样,UIScrollView有一个frame属 性,同时, ... 
- java实现定时任务
			Java中实现定时任务执行某一业务.具体操作如下: 1.定义初始化任务 2.任务业务操作 3.定义初始化方法 4.在web.xml中注册启动 5.定义具体执行时间 
- python基础8之自定义模块、if __name__==__main__:解释
			一.自定义模块与使用 python模块说明:类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个函数才 ... 
