阅读源码有助于陶冶情操,本文对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-paramcontextConfigLocation属性的值用于加载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父类简析的更多相关文章

  1. SpringMVC源码情操陶冶-DispatcherServlet类简析(一)

    阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...

  2. SpringMVC源码情操陶冶-HandlerAdapter适配器简析

    springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ...

  3. SpringMVC源码情操陶冶-DispatcherServlet

    本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...

  4. SpringMVC源码情操陶冶-DispatcherServlet简析(二)

    承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...

  5. SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器

    承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ...

  6. SpringMVC源码情操陶冶-FreeMarker之web配置

    前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...

  7. SpringMVC源码情操陶冶-AbstractHandlerMethodMapping

    承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ...

  8. SpringMVC源码情操陶冶-AbstractUrlHandlerMapping

    承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,前文主要讲解了如何获取handler处理对象,本文将针对beanName注册为handler对象作下解析 Abst ...

  9. SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器

    mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...

随机推荐

  1. NowCoderWannafly挑战赛5-可编程拖拉机比赛-向上取整和向下取整函数

    可编程拖拉机比赛 时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 65536K,其他语言131072K64bit IO Format: %lld 题目描述 "这个比赛,归根结底 ...

  2. 修改mysql密码的四种方法

    方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password('新密码'); 例子:my ...

  3. list容器的C++代码实现

    #include <iostream> using namespace std; template  <class T> class mylist;//前置声明 templat ...

  4. Spring框架学习笔记——各种异常、报错解决

    一.部分标签无法使用 原因:没有util导入命名空间 解决方法:在bean配置文件头部引用命名空间 <?xml version="1.0" encoding="UT ...

  5. 在 .NET中,一种更方便操作配置项的方法

    在应用程序的开发过程中,我们往往会为软件提供一些配置项,以允许软件根据配置项灵活来做事情,比如配置日志文件路径等,此外,我们还可以用配置项来为用户存储其偏好设置等. .NET 为我们默认提供了配置机制 ...

  6. [国嵌攻略][054][NandFlash驱动设计_写]

    Nand Flash支持按页写和随机写两种方式,在下面实现的是按页写.闪存在写数据时,只能写入1,不能写入0,所以写函数必须和擦除函数一起使用,并且擦除函数是按块擦除. /************** ...

  7. event.target与event.srcElement

    target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素.文档或窗口. 在标准浏览器下我们一般使用event.target就能解决,然而低版本IE浏览器总是会出些幺蛾子,这时候 ...

  8. 【编程技巧】java不使用第三个变量处理两个变量的交换

    public class SwapNum {public static void main(String[] args) {doSwapNum();}public static void doSwap ...

  9. UWP: 实现 UWP 应用自启动

    在上一篇文章中,我们实现了使用命令行来启动 UWP 应用,在这一篇文章中,我们会实现 UWP 应用自启用的实现,也即开机后或用户登陆后,应用自己启动.这些特性原来都是 Win32 程序所具备的,UWP ...

  10. Angular19 自定义表单控件

    1 需求 当开发者需要一个特定的表单控件时就需要自己开发一个和默认提供的表单控件用法相似的控件来作为表单控件:自定义的表单控件必须考虑模型和视图之间的数据怎么进行交互 2 官方文档 -> 点击前 ...