SpringMVC源码学习

SpringMVC的功能

SpringMVC的最大功能就是请求分发
通过 请求地址(uri) 找到对应的Controller类方法的映射路径,执行对应的方法。
那么就有两个问题

  1. 首先得找到Controller 对象
  2. 然后再执行Controller对象的方法

SpringMVC找Controller的流程

  1. 扫描整个项目(Spring 构建Bean 工厂的时候实现),定义一个Map集合
  2. 拿到所有加了@Controller注解的类(有很多种实现controller类的方法,这里只用注解说明)
  3. 遍历类里边的所有方法对象
  4. 判断是否加了@RequestMapper注解
  5. 把@RequestMapper注解的value 做为key,把找到的方法对象当做value ,存放在map集合中。
  6. 根据用户发送的请求拿到请求的uri(URL:http://localhost:8080/hello.do , URI:/hello.do)
  7. 根据请求的uri 去map集合找对应的方法对象(没有就返回404)
  8. 根据反射执行方法对象的方法

DispatcherServlet的继承关系图

通过类图发现DispatcherServlet 其实是HttpServlet的子类.
HttpServlet有几个重要的方法 doGet(),doPost(),service();

在这里插入图片描述
DispatcherServlet类并没有 doGet(),doPost(),service()方法 ,通过查找发现他的父类FrameworkServlet 有这些方法,但是他们调用的是同一个processRequest() 方法.
在这里插入图片描述在这里插入图片描述
找到processRequest() 方法后发现他进行一些处理后又调用了一个 doService() 方法 。

/**
	 * Process this request, publishing an event regardless of the outcome.
	 * <p>The actual event handling is performed by the abstract
	 * {@link #doService} template method.
	 */
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response);  //重点
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

我们去查找doService()方法,发现他其实是抽象方法,在DispatcherServlet类中重写了这个方法,并在方法中对公共的属性进行处理后调用了doDispatch()方法【今天的主角】


	/**
	 * Subclasses must implement this method to do the work of request handling,
	 * receiving a centralized callback for GET, POST, PUT and DELETE.
	 * <p>The contract is essentially the same as that for the commonly overridden
	 * {@code doGet} or {@code doPost} methods of HttpServlet.
	 * <p>This class intercepts calls to ensure that exception handling and
	 * event publication takes place.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 * @see javax.servlet.http.HttpServlet#doGet
	 * @see javax.servlet.http.HttpServlet#doPost
	 */
	protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;

/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					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());

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
			doDispatch(request, response); //重点
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

doDispatch()方法的源码分析!

  • WebAsyncManager在普通的开发中用不到,在异步处理的时候有用到,这一块我们先忽略
  • 关键部分:HandlerMappings,HandlerAdapter,HandlerExecutionChain对这几个熟悉之后基本上就懂了它到底在做什么
/**
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

  1. processedRequest意思为处理之后的请求,而它是由checkMultipart(request)方法处理的,我们看下这个方法。

    (1). 判断是不是multipart的请求,默认的判断方式是先判断是不是"POST"请求,然后判断"contentType"是否以multipart开头。
    (2).如果不满足multipart条件,直接返回request,也就是request没有做任何处理
    (3). 如果满足multipart基本条件, 将multipartRequest转换为StandardMultipartHttpServletRequest请求。

  2. getHandler() 方法是获取request的Handler的方法,handler被封装为HandlerExecutionChain,HandlerExecutionChain为handler方法加上拦截器的集合。我们定义的拦截器就在HandlerExecutionChain这个类中生效。
    HandlerExecutionChain为真正的Handler对象与Interceptor的组合类
    我们再去看看getHander() 方法

/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}
  1. handlerMappings 是HandlerMapping的一个list 集合他有几个元素
    requestMappingHandlerMapping (通过注解实现的Controller)
    beanNameHandlerMapping (通过实现接口实现的Controller)

  2. 通过Handler获取HandlerAdpater。HandlerAdpater作为适配器,真正干活的还是Handler,HandelAdpater做了很多的预处理,不过它的接口很简单。

public interface HandlerAdapter {
   // 判断当前的HandlerAdpater是否支持handler的实例,也就是当前的adapter能否使用传过来的Handler
    boolean supports(Object handler);
    // 使用传过来的handler来处理请求
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    //和Servlet的getLastModified功能基本一致
    long getLastModified(HttpServletRequest request, Object handler);

}

在DispatcherServlet中getHandlerAdapter的实现,则是遍历HandlerAdpaters,然后返回第一个支持handler的Adpater.

/**
	 * Return the HandlerAdapter for this handler object.
	 * @param handler the handler object to find an adapter for
	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
	 */
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

为什么要获取适配器?(采用适配器模式)
因为实现Controller 类的方法有很多(实现Controller接口,实现HttpRequest接口,通过注解)

  • 如果是以实现接口的方式,要调用请求对应的具体方法,只需要通过调用接口的方法的实现类就行了
  • 而通过注解方式实现的,则需要找到请求对应的方法对象利用反射才能实现调用这个方法

SpringMVC注解方式是怎么实现参数绑定的啦?

  • 获取到的方法对象getParameters()方法获取到所有参数

    Parameter[] parameters = method.getParameters();
    
  • 遍历获取到的参数数组
    因为参数类型不确定,所以要判断参数的类型,如果按照我们的想法会通过if-else 判断来实现,但是参数类型有很多,用if-else实现后果可想而知。
    所以这里引入了参数处理器来处理你的参数(策略设计模式),默认提供20 多种参数处理器,好处是每一种参数处理器都是一个对象,当要修改某个参数处理器的逻辑时,只修改这个对象就好了,不修改使用的地方。如果要新增也很方便。(更多请参考设计模式:策略设计模式)

  • 获取到支持参数处理的参数处理器,进行参数处理
    (1)获取到参数的名称。
    (2)然后从request中获取参数值
    (3)判空处理
    (4)返回参数值

  • 进行参数赋值。

  1. 执行HanderAdapter方法返回ModelAndView
  2. 将ModelAndView解析称Model
  3. 返回给客户端

自定义参数处理器

点击这里查看

SpringMVC执行流程

在这里插入图片描述

  • 一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
    DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping)
  • HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链
  • DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 - Handler 进行处理并返回 ModelAndView 给 DispatcherServlet
  • DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
  • DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
  • DispatcherServlet 将页面响应给用户

组件说明

DispatcherServlet:前端控制器

  • 用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,
  • 由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

HandlerMapping:处理器映射器

  • HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,
  • 例如:配置文件方式,实现接口方式,注解方式等。

Handler:处理器

  • Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
  • 由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

HandlAdapter:处理器适配器

  • 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

ViewResolver:视图解析器

  • View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,
  • 再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

View:视图

  • springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值