三歪肝出了期待已久的SpringMVC(一)

简介: 这篇SpringMVC被催了很久了,这阵子由于做整合系统的事,所以非常非常地忙。这周末早早就回了公司肝这篇文章了。

先简单聊聊SpringMVC

如果你们玩知乎,很可能会看到我的身影。我经常会去知乎水回答。在知乎有很多初学者都会问的一个问题:「我学习SpringMVC需要什么样的基础

我一定会让他们先学Servlet,再学SpringMVC的。虽然说我们在现实开发中几乎不会写原生Servlet的代码了,但我始终认为学完Servlet再学SpringMVC,对理解SpringMVC是有好处的。

三歪题外话:我当时在学SpringMVC之前其实已经接触过另外一个web框架(当然了Servlet也是学了的),那就是「大名鼎鼎」的Struts2。只要是Struts2有的功能,SpringMVC都会有。

当时初学Struts2的时候用的是XML配置的方式去开发的,再转到SpringMVC注解的时候,觉得SpringMVC真香。

Struts2在2020年已经不用学了,学SpringMVC的基础是Servlet,只要Servlet基础还行,上手SpringMVC应该不成问题。

从Servlet到SpringMVC,你会发现SpringMVC帮我们做了很多的东西,我们的代码肯定是没以前多了。

Servlet:

我们以前可能需要将传递进来的参数手动封装成一个Bean,然后继续往下传:

72.jpg

SpringMVC:

现在SpringMVC自动帮我们将参数封装成一个Bean

71.jpg

Servlet:

以前我们要导入其他的jar包去手动处理文件上传的细节:

70.jpg

SpringMVC:

现在SpringMVC上传文件用一个MultipartFile对象都给我们封装好了

69.jpg

........

说白了,在Servlet时期我们这些活都能干,只不过SpringMVC把很多东西都给屏蔽了,于是我们用起来就更加舒心了。

在学习SpringMVC的时候实际上也是学习这些功能是怎么用的而已,并不会太难。这次整理的SpringMVC电子书其实也是在讲SpringMVC是如何使用的

  • 比如说传递一个日期字符串来,SpringMVC默认是不能转成日期的,那我们可以怎么做来实现。
  • SpringMVC的文件上传是怎么使用的
  • SpringMVC的拦截器是怎么使用的
  • SpringMVC是怎么对参数绑定的
  • ......

68.jpg

现在「电子书」已经放出来了,但是别急,重头戏在后面。显然,通过上面的电子书是可以知道SpringMVC是怎么用的

但是这在面试的时候人家是不会问你SpringMVC的一些用法的,而SpringMVC面试问得最多的就是:SpringMVC请求处理的流程是怎么样的

其实也很简单,流程就是下面这张图:

67.jpg

再简化一点,可以发现流程不复杂

66.jpg

image.gif

在面试的时候甚至能一句话就讲完了,但这够吗,这是面试官想要的吗?那肯定不是。那我们想知道SpringMVC是做了什么吗?想的吧(不管你们想不想,反正三歪想看)。

65.jpg

由于想要主流程更加清晰一点,我会在源码添加部分注释以及删减部分的代码

以@ResponseBody和@RequestBody的Controller代码讲解为主,这是线上环境用得最多的

DispatcherServlet源码

首先我们看看DispatcherServlet的类结构,可以清楚地发现实际DispatcherServlet就是Servlet接口的一个子类(这也就是为什么网上这么多人说DispatcherServlet的原理实际上就是Servlet)

64.jpg

我们在DispatcherServlet类上可以看到很多熟悉的成员变量(组件),所以看下来,我们要的东西,DispatcherServlet可全都有

// 文件处理器
private MultipartResolver multipartResolver;
// 映射器
private List<HandlerMapping> handlerMappings;
// 适配器
private List<HandlerAdapter> handlerAdapters;
// 异常处理器
private List<HandlerExceptionResolver> handlerExceptionResolvers;
// 视图解析器
private List<ViewResolver> viewResolvers;

然后我们会发现它们在initStrategies()上初始化:

protected void initStrategies(ApplicationContext context) {
  initMultipartResolver(context);
  initLocaleResolver(context);
  initThemeResolver(context);
  initHandlerMappings(context);
  initHandlerAdapters(context);
  initHandlerExceptionResolvers(context);
  initRequestToViewNameTranslator(context);
  initViewResolvers(context);
  initFlashMapManager(context);
}

请求进到DispatcherServlet,其实全部都会打到doService()方法上。我们看看这个doService()方法做了啥:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  // 设置一些上下文...(省略一大部分)
  request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  try {
      // 调用doDispatch
   doDispatch(request, response);
  }
  finally {
   if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    if (attributesSnapshot != null) {
     restoreAttributesAfterInclude(request, attributesSnapshot);
    }
   }
  }
 }

所以请求会走到doDispatch(request, response);里边,我们再进去看看:

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
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
         // 得到对应的hanlder适配器
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         // 拦截前置处理
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         // 真实处理请求
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         // 视图解析器处理
         applyDefaultViewName(processedRequest, mv);
         // 拦截后置处理
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
   }
}

这里的流程跟我们上面的图的流程几乎是一致的了。我们从源码可以知道的是,原来SpringMVC的拦截器是在MappingHandler的时候一齐返回的,返回的是一个HandlerExecutionChain对象。这个对象也不难,我们看看:

public class HandlerExecutionChain {
 private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
  // 真实的handler
 private final Object handler;
  // 拦截器List
 private HandlerInterceptor[] interceptors;
 private List<HandlerInterceptor> interceptorList;
 private int interceptorIndex = -1;
}

OK,整体的流程我们是已经看完了,顺便要不我们去看看它是怎么找到handler的?三歪带着你们冲!我们点进去getHandler()后,发现它就把默认实现的Handler遍历一遍,然后选出合适的:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 // 遍历一遍默认的Handler实例,选出合适的就返回
  for (HandlerMapping hm : this.handlerMappings) {
    HandlerExecutionChain handler = hm.getHandler(request);
    if (handler != null) {
      return handler;
    }
  }
  return null;
}


目录
相关文章
|
前端开发 Java 调度
SpringMVC入门篇
SpringMVC入门篇
55 0
|
6月前
|
Java Spring Maven
Struts 2遇见Spring:这个组合如何颠覆你的Web开发?
【8月更文挑战第31天】在现代Web开发中,Struts 2与Spring的结合使用能显著增强应用的模块化和可维护性。本文将介绍如何整合这两个框架,并提供代码示例。首先,在`pom.xml`中添加Struts 2和Spring的依赖,然后在`struts.xml`中配置Struts 2以识别Spring插件。接着,在Spring配置文件中定义bean,并在Struts 2的Action类中使用`@Autowired`注解进行自动注入。
95 0
|
XML Java 数据格式
学习SpringMvc第三战-利用SpringMvc实现CRUD
学习SpringMvc第三战-利用SpringMvc实现CRUD
|
设计模式 前端开发 Java
一篇文章教会你SpringMVC
一篇文章教会你SpringMVC
65 0
|
设计模式 前端开发 JavaScript
SpringMVC实战入门教程,四天快速搞定springmvc框架!
SpringMVC 也叫Spring web mvc。是Spring 框架的一部分,是在Spring3.0 后发布的。 这里对SpringMVC框架进行一个简单的介绍: • springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。 • springmvc是一个基于mvc的web框架。 • springmvc 表现层:方便前后端数据的传输 • Spring MVC 拥有控制器,作用跟Struts类似,接收外部请求,解析参数传给服务层 MVC是指,C控制层,M模块层,V显示层这样的设计理念,而SSM框架里面SPRING MVC本身就是MVC框架,
291 0
|
设计模式 XML 开发框架
Spring框架中的SpringMvc三种方案实现的页面跳转(第十五课)
Spring框架中的SpringMvc三种方案实现的页面跳转(第十五课)
461 0
|
存储 JSON 前端开发
SpringMVC 程序开发:为什么要学SpringMVC?如何学SpringMVC?
SpringMVC 程序开发:为什么要学SpringMVC?如何学SpringMVC?
|
设计模式 前端开发 Java
Spring框架尝鲜
Spring 框架是当前 Java 领域应用最广的框架,它之所以那么成功,这主要是得益于它的设计理念。它的理念包括 IoC (Inversion of Control,控制反转) 和 AOP(Aspect Oriented Programming,面向切面编程)。下面我们就来一起学习下 Spring 这个优秀的开源框架。
114 0
【SpringMVC】框架搭建
【SpringMVC】框架搭建
100 0
【SpringMVC】框架搭建
|
XML 前端开发 Java
springmvc配置的全解析,致敬即将远去的mvc
springmvc配置的全解析,致敬即将远去的mvc
582 0
springmvc配置的全解析,致敬即将远去的mvc