SpringMVC运行原理(附源码追踪)


(直接看答案可点击上面 “4 一句话总结:SpringMVC运行原理 ”

1、什么是 SpringMVC ?

SpringMVC是对Servlet的封装
SpringMVC是对Servlet的封装
SpringMVC是对Servlet的封装

在没有使用SpringMVC框架的时候,如果想写一个Servlet请求分发器,我们会怎么写呢?

//接收除jsp外的所有请求,再进行分发
@WebServlet("/")
public class DispatcherServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String result = req.getParameter("control");
		if (result.equals("m1")) {
			method1();
		} else if (result.equals("m2")) {
			method2(req, resp);
		} else if (result.equals("m3")) {
			method3();
		}
	}

	private void method1() {
	}

	private void method2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.getRequestDispatcher("demo.jsp").forward(req, resp);
	}

	private void method3() {
	}

}

以上是我们自定义的DispatcherServlet,SpringMVC对Servlet的封装思想,跟上述代码是类似的。

2、SpringMVC 的重要组件

为了完成请求分发和处理,SpringMVC设计了以下组件

//前端控制器(分发器);相当于@WebServlet("/")
DispatcherServlet ds

//映射器,解析请求格式,判断要执行哪个方法
//相当于 if(req.getParameter("control").equals("m1"))
HandlerMapping hm

//适配器,负责调用具体的方法;相当于method1();
HandlerAdapter ha

//视图解析器,解析结果,准备跳转到具体的物理视图
//相当于req.getRequestDispatcher("demo.jsp").forward(req, resp);
ViewResolver vr 

3、SpringMVC 的 DispatcherServlet

当 SpringMVC 接收到请求时,首先会创建前端控制器DispatcherServlet
在这里插入图片描述
这里可以看到,DispatcherServlet 继承自 HttpServlet。而我们知道,Servlet首次加载时,调用顺序是 init() -> service() -> destroy(),我们一个方法一个方法地看。


3.1 init()

(留意中文注释)

	protected WebApplicationContext initWebApplicationContext() throws BeansException {
		// SpringMVC容器 (ConfigurableWebApplicationContext)
		WebApplicationContext wac = findWebApplicationContext();
		if (wac == null) {
			// No fixed context defined for this servlet - create a local one.
			// Spring容器
			WebApplicationContext parent =
					WebApplicationContextUtils.getWebApplicationContext(getServletContext());
			// spring容器是springmvc容器的父容器
			wac = createWebApplicationContext(parent);
		}

		if (!this.refreshEventReceived) {
			// Apparently not a ConfigurableApplicationContext with refresh support:
			// triggering initial onRefresh manually here.
			onRefresh(wac);
		}

		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;
	}

在这里插入图片描述

可以看到,init()主要是创建了springmvc容器。


3.2 service()

DispatcherServlet.class中

	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String requestUri = new UrlPathHelper().getRequestUri(request);
			logger.debug(
					"DispatcherServlet with name '" + getServletName() + "' received request for [" + requestUri + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		// 把请求参数做成一个HashMap保存
		Map attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			logger.debug("Taking snapshot of request attributes before include");
			attributesSnapshot = new HashMap();
			Enumeration attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		try {
			// 进入
			doDispatch(request, response);
		}
		finally {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		...
		
		try {
			ModelAndView mv = null;
			try {
				processedRequest = checkMultipart(request);

				// Determine handler for the current request.
				// mappedHandler相当于是 HandlerMapping映射器
				// 解析并匹配 handler
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Apply preHandle methods of registered interceptors.
				// 调用拦截器方法(与本题无关)
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if (interceptors != null) {
					for (int i = 0; i < interceptors.length; i++) {
						HandlerInterceptor interceptor = interceptors[i];
						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
							return;
						}
						interceptorIndex = i;
					}
				}

				// Actually invoke the handler.
				// 适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				// HandlerAdapter调用方法;生成 ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				...
		}
	}

值得注意的是,HandlerMapping 和 HandlerAdapter 都是在 DispatcherServlet 类的方法中被使用的。我们再来看看HandlerMapping和HandlerAdapter内部都做了什么?

选取 HandlerMapping 的实现类 SimpleUrlHandlerMapping 和 HandlerAdapter 的实现类 SimpleControllerHandlerAdapter 进行考察。

首先,在 springmvc.xml 配置文件中写的是:

<!-- 控制器 -->
<bean id="demo123" class="com.bjsxt.controller.DemoController"></bean>

<bean
	class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="urlMap">
		<map>
			<!-- 解析出来的控制器逻辑名 -->
			<entry key="demo" value-ref="demo123"></entry>
		</map>
	</property>
</bean>

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

3.3 HandlerMapping 实现类

当程序加载springmvc.xml时,SimpleUrlHandlerMapping.class中

	protected void registerHandlers(Map urlMap) throws BeansException {
		if (urlMap.isEmpty()) {
			logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
		}
		else {
			Iterator it = urlMap.keySet().iterator();
			while (it.hasNext()) {
				String url = (String) it.next();
				Object handler = urlMap.get(url);
				// Prepend with slash if not already present.
				// 这里表示上述 key="demo"中无论是否加/,最终都有/
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				// Remove whitespace from handler bean name.
				if (handler instanceof String) {
					handler = ((String) handler).trim();
				}
				//url对应“/demo”;handler对应“demo123”
				//进入
				registerHandler(url, handler);
			}
		}
	}
	protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
		Assert.notNull(urlPath, "URL path must not be null");
		Assert.notNull(handler, "Handler object must not be null");
		Object resolvedHandler = handler;

		// Eagerly resolve handler if referencing singleton via name.
		if (!this.lazyInitHandlers && handler instanceof String) {
			String handlerName = (String) handler;
			if (getApplicationContext().isSingleton(handlerName)) {
				// 这里拿到了 id="demo123"的 bean,即对应的控制器
				resolvedHandler = getApplicationContext().getBean(handlerName);
			}
		}

		Object mappedHandler = this.handlerMap.get(urlPath);
		if (mappedHandler != null) {
			if (mappedHandler != resolvedHandler) {
				throw new IllegalStateException(
						"Cannot map handler [" + handler + "] to URL path [" + urlPath +
						"]: There is already handler [" + resolvedHandler + "] mapped.");
			}
		}
		else {
			if (urlPath.equals("/")) {
				if (logger.isDebugEnabled()) {
					logger.debug("Root mapping to handler [" + resolvedHandler + "]");
				}
				setRootHandler(resolvedHandler);
			}
			else if (urlPath.equals("/*")) {
				if (logger.isDebugEnabled()) {
					logger.debug("Default mapping to handler [" + resolvedHandler + "]");
				}
				setDefaultHandler(resolvedHandler);
			}
			else {
				// 做一个(解析后的)url路径和对应控制器方法的映射表
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isDebugEnabled()) {
					logger.debug("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]");
				}
			}
		}
	}

3.4 HandlerAdapter 实现类

SimpleControllerHandlerAdapter.class

	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 调用控制器方法
		// 本例中是调用(id="demo123"的)com.bjsxt.controller.DemoController中的方法
		return ((Controller) handler).handleRequest(request, response);
	}

4、一句话总结:SpringMVC运行原理

①. 当在 web.xml 中设置 DispatcherServlet 的 url-pattern 为 / 时,用户发起请求,DispatcherServlet接收。
②. DispatcherServlet 调用 HandlerMapping 解析 URL,并找到对应的控制器方法 (Handler对象)
③. DispatcherServlet 调用 HandlerAdapter,HandlerAdapter 调用 Controller 中的方法
④. 方法执行完成后会返回一个 ModelAndView 对象
⑤. ViewResovler 对 ModelAndView 进行视图解析,返回View
⑥. DispatcherServlet 对 View(如jsp) 进行渲染(将模型数据填充至视图中)
⑦. DispatcherServlet 响应用户
(DispatcherServlet - 前端控制器;Controller - 后端控制器)

5、前端设计模式

DispatcherServlet 的设计使用了前端设计模式。
围绕 (web.xml中配置的) DispatcherServlet 进行初始化,即 DispatcherServlet 做中央处理器。

  • 从 DispatcherServlet 中的 initStrategies 方法可以清楚地看到:
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值