Spring mvc原理解析

1.@RequestMapping
  1. @RequestMapping注解来映射URl,返回值会通过视图解析器解析为实际的物理试图,对于org.springframework.web.servlet.view.InternalResourceViewResolver试图通过prefix+returnVal+后缀这样的方式得到实际的物理试图,然后做转发操作.
<bean class=*"org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name=􏰙*prefix*􏰧 *value=*􏰙*/WEB-INF/view/"></property>
  <property name=*"suffix" value=".jsp"></property>
</bean>
2.@ModelAttribute

被@ModelAttribute标记的方法,会在每一个目标方法执行之前被springmvc调用

    @ModelAttribute
    public void getUserInfo(@RequestParam(value = "id",required = false) Integer id, Map<String,Object> map){
         System.out.println("xxxxxx");
        if(id!=null){
            Person person=new Person("x","1");
            System.out.println(person);
            map.put("person",person);
        }
    }


    @RequestMapping("/demo/xx")
    public ModelAndView demo(HttpServletRequest request, HttpServletResponse response,Person person ){
        System.out.println("x");
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.addObject(person);
        return modelAndView;
    }

运行流程:

  1. 执行@ModelAttribute注解修饰的方法,从数据库中取出对象,把对象放到map中,
  2. springmvc从map取出User对象,并把表单的请求参数赋值到给该对象的对象属性
  3. springmvc吧上述对象传入目标方法参数

源码分析

  1. 首先会调用@ModelAttribute 注解修饰的方法, 实际上把@ModelAttribute方法中的map 的数据保存到implicitModel中
  2. 解析请求参数中的目标参数,实际上该目标参数 来自于WebDateBinder对象的target属性
    WebDateBinder=bingObject(target对象)+attrName
    1. 创建WebDateBinder对象
      • 确定objectName属性:若传入的attrNa me属性值为"",则objectName为类型首字母小写,如果目标方法的pojo属性使用了@ModelAttribute来修饰,则attrName值为@ModelAttribute的value属性值
      • 确定target属性
        1. 在implicitModel中查找attrName对应的属性值,若存在,OK
        2. 若不存在则验证当前handler是否使用了@SessionAttributes进行修饰,若使用了,则尝试从session中获取attrName所对应的属性值,若session中没有对应的属性值,则抛出异常
        3. 若Handler没有使用@SessionAttributes修饰,或@SessionAttributes中没有使用value值指定的key和attrName相匹配,则通过反射创建POJO对象
    2. springmvc把表单的请求参数赋给了WebDateBinder的target对象
    3. springmvc会把WebDateBinder的attrName和target给到implicitModel,传到request域中
    4. 把WebDateBinder的target作为参数传递给目标方法入参
3.springmvc是怎么确定目标对象方法POJO类型入参的过程的?
  1. 确定一个key
    • 若目标方法的pojo类型的参数没有@ModelAttribute作为修饰,则key为POJO类型的首字母小写
    • 若使用了@ModelAttribute来修饰,则key为@ModelAttribute注解的value属性值
  2. 在implicitModel中查找key对象的对象,若存在,则作为入参传入
    1. 若在@ModelAttribute标记的方法中在map中保存在,且key和之前确定的key一致,则会获取到
  3. 若implicitModel获取不到key对应的对象,则会检查当前的handler是否使用@SessionAttributes注解修饰,若使用了该注解,且@SessionAttributes注解的value包含了key,则会从HttpSession获取到key所对应的value的值,若存在则直接传入到目标对象的入参中,若不存在则将抛出异常
  4. 若handler没有标识@SessionAttributes注解或@SessionAttributes注解的@Value不包含key ,则通过反射创建POJO类型的参数,传入目标方法的参数
  5. springmvc会把key和POJO类型的对象保存到implicitModel中,进而保存到request中
4.springmvc数据绑定流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLt64TnN-1575211185165)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201140712842.png)]

流程

  1. Springmvc通过反射机制对目标处理方法进行解析,并将请求消息绑定到处理方法入参中.

  2. Springmvc会将ServletRequest对象及目标处理方法的参数对象实例传递到DataBinder,DataBinder调用装配在Spring web上下文的ConversionService组件进行数据类型转换,数据格式化工作,并将ServletRequest中的消息填充到参数对象中,然后再调用Vaildator组件已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定结果BindingResult对象.

  3. BindingResult对象包含了已完成数据绑定参数对象,还包含了相对应的校验错误对象.

5.@RequestBody @ResponseBody原理

工作原理图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TPrzMpcm-1575211185166)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201153135250.png)]

  1. 从请求报文先转为HttpInputMessage再由HttpMessgetConverter转为我们需要的java对象
  2. spingmvc返回一个对象先转为 HttpMessgetConverter再转HttpOutPutMessage再响应信息
public interface HttpInputMessage extends HttpMessage {
 //把请求信息转为输入流
	InputStream getBody() throws IOException;
}
public interface HttpOutputMessage extends HttpMessage {
  //把请求信息转为输出流
	OutputStream getBody() throws IOException;
}

核心在于HttpMessgetConverter,SpringMVC处理请求和响应时,支持多种类型的请求参数和返回类型,而此种功能的实现就需要对HTTP消息体和参数及返回值进行转换,为此SpringMVC提供了大量的转换类,所有转换类都实现了HttpMessageConverter接口。

6拦截器执行流程

拦截器有三个方法

  1. preHandle 在目标方法之前调用
  2. postHandle 调用目标方法后,渲染试图之前
  3. afterCompletion 渲染试图后
  4. 拦截器可以通过配置作用的路径<mvc:mapping ><mvc:exclude-mapping>配置包含关系
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;

     ----省略代码-----
				//在目标方法之前调用
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 调用目标方法
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

         applyDefaultViewName(processedRequest, mv);
     			//调用目标方法后,渲染试图之前调用
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      //渲染试图
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
  
}
//渲染视图
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			------....---
		}

		if (mappedHandler != null) {
			// 渲染视图完之后调用
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

7拦截器执行顺序
  1. 正常流程

第一个拦截器preHandle—》第二个拦截器preHandle–》目标方法–》第二个拦截器postHandle–》第一个拦截器postHandle --》第二个拦截器afterCompletion–》第一个拦截器afterCompletion

  1. 第二个拦截器preHandle返回false

第一个拦截器preHandle—》第二个拦截器preHandle–》第一个拦截器afterCompletion

  1. preHandle正序,afterCompletion和postHandle倒序
8. springmvc执行流程图

在这里插入图片描述
9.springmvc源码解析

核心方法在于DispatcherServlet中的doDispatch

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

         // 获取当前请求的HandlerExecutionChain,根据handleMapping获取
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         //获取.HandlerAdapter
         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;
         }

         // 调用目标方法
         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) {
        
         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. 先获取一个mappingHandler,类型就是HandleExecutionChain,处理器调用链(包含了拦截器,目标方法和handler ),通过handlerMapping获取

    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;
    }
    
  2. 获取到hadlerAdapter,( 包含messageConver,数据转换,数据格式化,校验等等)

  3. 调用目标方法返回ModelAndView

  4. 调用拦截的applyPostHandle

  5. 处理视图processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {

   boolean errorView = false;
  //如果有异常就通过handlerExceptionResolvers来处理
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // 如果没异常就进行渲染试图
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
   }

   if (mappedHandler != null) {
      // 渲染完调用 拦截器afterCompletion
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}
  • 会先判断是否有异常,如果有异常就通过handlerExceptionResolvers来处理

  • 如果没异常就进行渲染试图

    • 会先通过ViewResolvers得到View
    • 下一步渲染视图
  • 渲染完调用 拦截器afterCompletion

  • 会先判断是否有异常,如果有异常就通过handlerExceptionResolvers来处理

  • 如果没异常就进行渲染试图

    • 会先通过ViewResolvers得到View
    • 下一步渲染视图
  • 渲染完调用 拦截器afterCompletion

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值