SpringMVC之浅析上下文初始化(二)
来源:互联网 发布:租用临时备案域名 编辑:程序博客网 时间:2024/05/22 06:40
说明:本文所用的SpringMVC版本为4.3.4.RELEASE,应用服务器为TomCat8.0.33。
在上一篇文章中(点这里查看)我们说了ContextLoaderListener初始化Web上下文的过程,这篇文章中我们说一下DispatcherServlet初始化上下文的过程。我们先来看一下DispatcherServlet相关的UML类图:
从上图中我们可以看到DispatcherServlet也是一个HttpServlet的一个子类,并间接的实现了ApplicationContextAware这个接口。DispatcherServlet既然是一个Servlet的实现类,那么它也是遵守Servlet的生命周期的。也会有实例化、初始化(执行init方法)、接收请求处理请求(执行service方法)、销毁(执行destroy()方法)。所以DispatcherServlet的初始化过程,我们也是从init()这个方法开始(注意:我们这里说的初始化时执行init()方法,和类的初始化不是一回事,要区分开)。在开始之前我们还是要看一下相关的一些堆栈信息。
为什么要把这些堆栈信息截出来呢?因为这些堆栈信息是相当重要的东西。这也是TomCat的体系架构中很重要的一些类和组成部分。从上图中我们可以看到init()这个方法是在StandarWrapper中的initServlet中被调用的(StandarWrapper代表着一个Servlet)。OK,下面进入我们的正题吧。
GenericServlet#init
从上面的UML图中和堆栈信息那张图中我们可以看到Servlet的第一个实现类是GenericServlet,并且最先调用的也是GenericServlet中的init方法,所以我们首先分析的也就是它了。我们先看一下这个方法的源码:
@Override public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }源码中的东西非常简单啊,只有两句话话,一句是给ServletConfig赋值,一句是调用无参的init()方法。这里的ServletConfig的实现类为:StandardWrapperFacade。下面我们进入到无参的init方法中看看这个方法中执行了哪些内容。我们在GenericServlet这个类的init方法中发现代码是这样的:
public void init() throws ServletException { // NOOP by default }WHAT?空实现?空实现我们还怎么玩?别着急,我们在HttpServletBean中找到了一个重写的init方法。下面我们进转到HttpServletBean中去看一下。
HttpServletBean#init
我们看一下init这个方法的源码(省略了一些不重要的代码):
public final void init() throws ServletException {try {//创建属性编辑器类 (1)PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);//创建一个编辑属性值的BeanWrapper (2)BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//用Resource加载Resource类型的属性(3)ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));//初始化一些其他的BeanWrapper信息(其实是一个空方法)initBeanWrapper(bw);//为DispatcherServlet中的属性赋值bw.setPropertyValues(pvs, true);}catch (BeansException ex) {throw ex;}// Let subclasses do whatever initialization they like.//继续执行初始化的动作(4)initServletBean();}简单来说这个方法中干了这样的两件事,一是为DispatcherServlet中的属性赋值,二是调用initServletBean()方法。这里有几个地方需要说明一下,PropertyValue是一个存放一个对象的属性名字和值的类,即它保存一个bean的单独属性的值信息。PropertyValues是PropertyValue的集合。在(1)处创建了一个PropertyValues的对象,它的具体实现类是ServletConfigPropertyValues,从名字我们也能看出来这是一个获取Servlet配置信息的PropertyValues的属性值的结合,即它存放的是在Servlet中配置的信息。在这里它还做了另外的一件事,即校验一些必须配置的属性信息。在(2)处创建了一个BeanWrapper的实现类,BeanWrapper也是Spring框架中很重要的一个组件类,它可以用来编辑对象的属性值,所以这里创建的是一个编辑DispatcherServlet属性值的BeanWrapper的实现类。(3)处,如果有Resource类型的资源,则用相应的ResourceLoader来进行处理。bw.setPropertyValues这里就是给DispatcherServlet中的属性进行赋值的动作了。说了那么多,到底会给哪些属性赋值呢?又赋值的是哪些属性呢?举几个例子说明一下吧:
我们在web.xml中配置DispatcherServlet的时候,如果更改默认的SpringMVC配置文件的话,一般都会这样配置:
<servlet> <servlet-name>spring-miscellaneous</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-miscellaneous-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>注意,我们在<servlet>标签中添加了一个<init-param>的标签,指定了SpringMVC的配置文件的位置,我们在FrameworkServlet中发现有这样的一个属性contextConfigLocation,和我们的初始化参数的名字一样,然后我们翻遍代码也找不到有调用setContextConfigLocation这个方法的地方,那什么时候给这个属性进行赋值的呢?答案很明显了,就是在调用bw.setPropertyValues(pvs, true);的时候了。还有detectAllHandlerMappings等等属性,也是这样进行赋值的。(4)这个方法是一个很重要的方法,主要的初始化就是在这里完成。我们看一下这个方法的源码:
FrameworkServlet#initServletBean
去掉一些不重要的代码。
protected final void initServletBean() throws ServletException {try {this.webApplicationContext = initWebApplicationContext();//空实现initFrameworkServlet();}}这个方法里面的内容也是很简单,其实就一句话,因为initFrameworkServlet是一个空实现的方法。最主要的方法是initWebApplicationContext,这个方法是DispatcherServlet初始化最重要的一个方法了。
FrameworkServlet#initWebApplicationContext
initWebApplicationContext中的注意源码如下:
protected WebApplicationContext initWebApplicationContext() {//根据ServletContext获取根上下文即ContextLoaderListener中创建的XmlWebApplicationContextWebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;//如果有webApplicationContext注入的话,则使用注入的webApplicationContextif (this.webApplicationContext != null) {wac = this.webApplicationContext;//如果注入的webApplicationContext是ConfigurableWebApplicationContext的子类if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;//如果注入的webApplicationContext还没有被激活if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);}//配置webApplicationContext中的内容,并调用refresh()方法,进行初始化configureAndRefreshWebApplicationContext(cwac);}}}//如果wac为null的话,即没有注入webApplicationContextif (wac == null) {//则从ServletContext中查找配置的WebApplicationContextwac = findWebApplicationContext();}//如果上下文中也没有WebApplicationContext,则创建WebApplicationContextif (wac == null) {wac = createWebApplicationContext(rootContext);}//如果还没有调用refresh()方法的话,则调用onRefresh方法if (!this.refreshEventReceived) {onRefresh(wac);}//将WebApplicationContext放入到ServletContext中if (this.publishContext) {String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;}在这个方法中主要干了下面几件事:
- 获取根WebApplicationContext(即在ContextLoaderListener中创建的XmlWebApplicationContext,可以把它当做是一个父容器)
- 如果在实例化DispatcherServlet的时候,如果有传入WebApplicationContext,则使用传入的WebApplicationContext。
- 如果2没有,则从ServletContext中查找配置的WebApplicationContext,
- 如果3也没有找到,则创建WebApplicationContext
- 调用onRefresh方法,进行一系列的初始化动作
- 将初始化之后的WebApplicationContext放入到ServletContext中(key是FrameworkServlet.class.getName() + ".CONTEXT."+servletName)
因为我们是在实例化DispatcherServlet的时候,调用的是默认的无参构造函数,所以在实例化的时候没有传入的WebApplicationContext,我们也没有在ServletContext配置WebApplicationContext,所以这里我们直接进入到createWebApplicationContext这个方法中,进行创建WebApplicationContext。
FrameworkServlet#createWebApplicationContext
createWebApplicationContext中的主要源码如下:protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {//获取上下文类Class<?> contextClass = getContextClass();//这个类必须是ConfigurableWebApplicationContext的子类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());//设置父ApplicationContext即设置父容器wac.setParent(parent);//设置contextConfigLocation中的配置文件wac.setConfigLocation(getContextConfigLocation());//初始化WebApplicationContextconfigureAndRefreshWebApplicationContext(wac);return wac;}这个方法中主要干了这几件事:
- 获取WebApplicationContext上下文的类。
- 校验是不是ConfigurableWebApplicationContext的子类。
- 实例化WebApplicationContext
- 设置父容器
- 设置SpringMVC的配置文件
- 进行WebApplicationContext相关的一些其他配置,并调用refresh方法。
我们先来看一下getContextClass这个方法。
public Class<?> getContextClass() {return this.contextClass;}getContextClass这个方法也很简单,就是获取contextClass的值。contextClass这个属性有一个默认的值:XmlWebApplicationContext.class。如果没有在web.xml的<servlet>标签中进行其他值的配置的话,则contextClass就取默认值XmlWebApplicationContext.class。XmlWebApplicationContext是一个实现了ConfigurableWebApplicationContext接口的一个类,下面我们需要分析的一个方法是configureAndRefreshWebApplicationContext
FrameworkServlet#configureAndRefreshWebApplicationContext
其主要源码如下:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {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());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);wac.refresh();}上面这些代码中主要做了这几件事:
- 设置id值(还没完全明白它的具体实际作用)
- 设置ServletContext
- 设置ServletConfig
- 设置nameSpace
- 设置一些监听器
- 初始化一些属性信息
- 如果有配置ApplicationContextInitializer相关的类,则调用ApplicationContextInitializer的initialize方法进行一些初始化的操作。
- 调用refresh方法。这个方法就是读取SpringMVC配置文件,解析bean、组装bean等等一系列操作了。
关于wac.refresh()方法的调用,我们这里先不分析的。在Spring的源码分析中再进行分析。接下来我们就回到org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext这个方法的这一段代码中:
if (!this.refreshEventReceived) {onRefresh(wac);}onRefresh()这个方法的实现是在DispatcherServlet中的。
DispatcherServlet#onRefresh
其源码内容如下:
@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}从上面的源码中我们发现它只是调用了initStrategies这个方法。关于这个方法的分析,请点击这里(SpringMVC之浅析组件初始化过程)。
OK了,到这里我们关于DispatcherServlet初始化的主干流程的分析就先结束了。接着会做一些枝干流程的分析的工作(即一些Spring的生命周期接口的一些实现类)。
PS:感觉最近CSDN的这个编辑器总是失焦呢、、、
阅读全文
0 0
- SpringMVC之浅析上下文初始化(二)
- SpringMVC之浅析上下文初始化(一)
- SpringMVC之浅析组件初始化过程
- 驱动那些事儿之二:驱动模块初始化浅析
- SpringMVC之原理浅析
- springmvc 之DispatcherServlet初始化
- SpringMvc 浅析 之 URL Action
- SpringMvc 浅析 之 错误处理
- SpringMVC之DispatcherServlet初始化顺序。
- SpringMVC学习笔记(一) DispatcherServlet初始化详解(应用上下文的初始化)
- 做一个合格的程序猿之浅析Spring IoC源码(二)BeanFactory初始化
- 做一个合格的程序猿之浅析Spring IoC源码(二)BeanFactory初始化
- gsoap浅析之二
- springMVC(二),每天学习一点点~~~~~WebApplicationContext(父子上下文)
- Servlet和SpringMVC的初始化及请求处理过程浅析
- web上下文,spring上下文,springmvc上下文
- 浅析Android MediaProvider之二
- 浅析Android MediaProvider之二
- 38 WebGL针对单独的顶点坐标绘制组成模型
- Ubuntu14.04上安装ROS
- 后端框架_数据库技术mysql
- HashSet哈希值
- 字符数组、String类、StringBuffer三者的相互转换
- SpringMVC之浅析上下文初始化(二)
- MyFlag Step11 :menupath的移植工作
- java中的乱码及编码问题
- 有两个顺序表LA,LB,其元素均为非递减有序排列,编写算法将它们合并成一个顺序表LC,要求LC也是非递减有序排列。
- 【Unity优化】内存优化
- Python基础(四)- 函数
- Rxjava学习笔记-01
- 需求调研该怎么做
- 链表的简单实现