springMVC 源码解析之器用分析法-----springmvc之器

声明:此博文为个人学习总结,如有错误疏漏之处,欢迎各位批评指正!


采用“器用分析法”对springmvc进行源码解析:

  • 器:顾名思义就是要分析的目标,即springmvc本身的创建过程
  • 用:即如何使用,即springmvc是如何工作的

使用“器用分析法”可以在一个复杂的类的很多看似杂乱无章的方法中快速梳理出头绪,不仅仅可以用来分析springmvc的源码,用来分析其他的源码也是可以的。

本博文主要是从“器”的角度对springmvc进行解析


首先从springmvc的前置控制器即DispatcherServlet 入手:

DispatcherServlet的继承结构:
DispatcherServlet的继承结构

从springmvc的整体结构分析:
springmvc整体结构图

在上图可以看出继承结构中有五个类:

  • GenericServlet
  • HttpServlet
  • HttpServletBean
  • FrameworkServlet
  • DispatcherServlet

在springmvc中主要对后三个类做出论述:HttpServletBean、FrameworkServlet、DispatcherServlet

其中HttpServletBean和FrameworkServlet直接实现了三个接口:

  • EnvironmentCapable
  • EnvironmentAware
  • ApplicationContextAware

在英文中Aware和Capable的意思如下:
   Aware : 感知,意识到的,察觉到的
   Capable : 有能力的胜任的

对后缀 XXXAware 的通俗解析如下:
  如果你想使用spring中的某些东西,比如XXX,就可以通过实现 XXXAware 接口,告诉spring,spring会通过实现接口的方法 setXXX 将XXX送过来,我们只需要接收就可以了,XXXAware 这里很容易理解,就是对XXX感知。

在这里实现了ApplicationContextAware接口,所以spring会通过调用setApplicationContext将ApplicationContext传给我们。实现EnvironmentAware接口也是同样的道理。

EnvironmentCapable也就是有Environment的能力,EnvironmentCapable会提供唯一的方法Environment getEnvironment(),这里也很容易看出来,他就是提供了一个Environment,ApplicationContext相信大家都已经很熟悉了

下来我们来分析分析Environment是一个什么东西。

我们提供一个普通的controllelr实现EnvironmentAware接口,通过断点分析:

package net.csdn.handler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController implements EnvironmentAware{
	private final Log logger = LogFactory.getLog(HelloController.class);
	private Environment environment = null;
	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}
	@RequestMapping("hello")
	public String hello(Model model){
		logger.info(" before.");
		model.addAttribute("message","hello springmvc !!!");//在此处设置断点
		logger.info(" after.");
		return "hello";
	}
}

注:简单环境搭建可以参考我的上一篇博文

这里断点调试查看environment 中的值,可以看到如下内容:

断点调试 environment

可以看到propertySources中包含了五个属性,下来对这五个属性分别来做说明:

  • ServletConfigPropertySource

    继续看ServletConfigPropertySource的内部结构
    ServletConfigPropertySource 内部结构

    可以看到ServletConfigPropertySource的source类型是StandardWrapperFacade,也就是Tomcat中定义的一个ServletConfig类型,所以这里就直接认为ServletConfigPropertySource封装的就是ServletConfig

继续查看config中的内容,可以看到之前在web.xml中配置的contextConfigLocation

config 内部分析

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>springmvcSourceAnalysis</display-name>

  <servlet>
  	<servlet-name>SourceAnalysis</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<!-- 配置springmvc配置文件的位置 -->
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:springmvc.xml</param-value>
  	</init-param>
  	<!-- 设置启动加载 -->
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>SourceAnalysis</servlet-name>
	<url-pattern>/</url-pattern>  
  </servlet-mapping>
  
</web-app>

至此,认为ServletConfigPropertySource封装的就是ServletConfig也是毋庸置疑的了。

  • ServletContextPropertySource
    ServletContextPropertySource 内部结构
    这里很容易就能看出ServletContextPropertySource封装的是ServletContext

  • JndiPropertySource 这里没有使用到Jndi 就不做分析了

  • MapPropertySource
    MapPropertySource
      MapPropertySource存放的是虚拟机的属性:包括java版本,所用操作系统的名称、版本号,用户主目录,临时目录等信息

  • SystemEnvironmentPropertySource
    SystemEnvironmentPropertySource
    通过上图可以很清楚的看出SystemEnvironmentPropertySource中存放的是环境变量


分析完了整体的结构,下来我们对HttpServletBean、FrameworkServlet、DispatcherServlet这三个类来做具体源码分析:

HttpServletBean

首先来看HttpServletBean的init方法:

//public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// 将Servlet中配置的参数设置到相应的属性
		try {
			//将Servlet中配置的参数封装到pvs变量中,其中第二个参数为必填项,否则会报错
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			//初始化一个BeanWrapper 对象 bw 
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			//initBeanWrapper()为模板方法,由子类调用,来做一些初始化的工作,这里的入参bw中封装了DispatcherServlet对象,代表DispatcherServlet
			initBeanWrapper(bw);
			//将配置的初始化值 设置到DispatcherServlet
			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() 也是一个模板方法,由子类调用,是子类初始化的入口方法   
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

可以看出HttpServletBean继承自HttpServlet ,只做了一件事情,就是将Servlet中配置的参数设置到相应的属性,其余交由子类去完成。

FrameworkServlet

由上文可知,FrameworkServlet入口方法是,initServletBean(),从入口方法看起

//public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
@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 {
			//***核心代码***
			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");
		}
	}

可以看出这里的核心代码仅仅只有两句:

this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();

都是做了初始化的工作,分别初始化WebApplicationContext和FrameworkServlet
  但是这里的initFrameworkServlet()是一个模板方法,本应由子类调用做一些初始化的工作,但是子类并没有使用它,所以这里着重讨论initWebApplicationContext(),可知FrameworkServlet 在构建的过程中的主要作用就是初始化了WebApplicationContext

下来对initWebApplicationContext()进行说明

//public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
protected WebApplicationContext initWebApplicationContext() {
		//第一件事:获取了spring的根容器,rootContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		//第二件事:设置webApplicationContext ,并根据情况调用onRefresh()方法
		if (this.webApplicationContext != null) {
			//如果已经通过构造方法设置了webApplicationContext 那么直接去使用就可以了
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					//context 还没有被刷新,例如:提供设置parent context、设置application context id 的服务等等
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> 
						// set the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}
		
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			//第三件事:将webApplicationContext 设置到ServletContext
			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;
	}

//next todo

分析springmvc中的三个类 (引入源码 ,图片说明 进行分析)
   HttpServletBean
   FrameworkServlet
   DispatcherServlet

小结…未完待续!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值