
手写了一个Spring MVC
手写一个Spring MVC
我们先手写一个Spring MVC,让你对Spring MVC的整体实现有一个基本的认识
github代码:https://github.com/erlieStar/servlet-learningv3分支
用的是servlet 3.0所以就不用web.xml了,全程注解
定义注解
定义中央控制器
基本上所有的逻辑都在这个类中,主要流程如下
- 创建DispatcherServlet的时候,tomcat会调用init()方法,在里面初始化url和对应的处理方法的映射关系
- 当有请求来的时候,从uriInvokeInfoMap中拿对应的方法,如果有对应的方法,反射调用方法,拿到页面名字,拼接页面地址,转发到相应的页面,否则返回404
将方法及其对应的类做了一下封装
开始测试
访问如下连接,页面正常显示
如果你看过Spring MVC的源码,本质上也是存取map的过程
- 启动的时候,将url和其对应的方法存到map中
- 有请求的时候,根据url从map中找到对应的方法,执行方法返回结果
Spring MVC执行流程
上图展示了一个Spring MVC的执行流程
- 用户发送请求到DispatcherServlet
- DispatcherServlet从HandlerMapping中找到对应的handler
- DispatcherServlet找到能执行handler的HandlerAdapter
- HandlerAdapter执行Handler,并将Handler返回的ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView转交给ViewResolver
- ViewResolver返回对应的View对象
- DispatcherServlet根据View对象进行视图渲染(将模型数据填充到视图中)
- DispatcherServlet将View响应给用户
DispatcherServlet
DispatcherServlet继承关系如下图,HttpServlet类及其父类定义在javax包中,其余是定义在Spring包中
可以看到DispatcherServlet本质上是一个HttpServlet。
想想我们之前不用Spring MVC如何写一个web程序?
- 写一个类继承HttpServlet,重写doGet或者doPost方法
- 用@WebServlet注解定义请求的路径
可以看到以前是针对一个请求创建一个HttpServlet,现在是将所有的请求都转发到DispatcherServlet,然后由DispatcherServlet反射调用controller的方法,然后将结果返回给用户
这么做的目的主要是统一管理web请求的处理流程,Struts和Spring MVC都是这种设计
当Spring容器启动或者刷新的时候,DispatcherServlet会初始化后续常用的组件,如HandlerMapping,HandlerAdapter,ViewResolver等。
HandlerMapping
HandlerMapping主要就是根据请求的url找到对应的handler,看到这你可能会想,这个叫ControllerMapping是不是更好一点?并不是,之所以说Handler,是因为在Spring MVC中,Handler常见的实现方式有三种,虽然一般我们只用@RequestMapping注解
实现Controller接口
访问http://localhost:8080/index,页面输出IndexController,这里需要说明的有2点
- 当Handler放回的ModelAndView为null时,后续ViewResolver查找View,View进行渲染的过程会被省略
- @Component注解的value值必须以/开头,后续会说原因
实现HttpRequestHandler接口
访问http://localhost:8080/address,页面输出AddressController
使用@RequestMapping注解
这种方式大家应该很熟悉了,就不再介绍了
既然Handler的实现有很多种,相应的查找方式也应该有很多中,Spring MVC中有3个HandlerMapping的实现类,对应不同的映射策略
映射策略 | handler实现方式 | 查找实现类 |
简单url映射 | 实现HttpRequestHandler接口;实现Controller接口,指定路径 | SimpleUrlHandlerMapping |
BeanName映射 | 实现Controller接口不指定路径 | BeanNameUrlHandlerMapping |
@RequestMapping映射 | 使用@RequestMapping注解 | RequestMappingHandlerMapping |
想深入了解的看我这篇文章,本文的重点不是分析各个组件,所以不深入了https://blog.csdn.net/zzti_erlie/article/details/106168046
HandlerAdapter
其实我刚开始就不明白为啥要有HandlerAdapter这个组件,既然已经找到的handler,直接调用handler的方法不就行了。
知道我知道了handler的实现方式有很多种时,才意识到HandlerAdapter是必须的,因为每种handler,调用逻辑是不一样的。有了HandlerAdapter可以解耦,不然又是一堆if else
例如,实现Controller接口的handler,调用逻辑是执行handleRequest方法,用@RequestMapping的handler,是通过反射来执行方法。
常用的HandlerAdapter如下
类名 | 作用 |
HttpRequestHandlerAdapter | 执行实现了HttpRequestHandler接口的Handler |
SimpleControllerHandlerAdapter | 执行实现了Controller接口的Handler |
RequestMappingHandlerAdapter | 执行Handler类型是HandlerMethod及其子类的Handler,RequestMappingHandlerMapping返回的Handler是HandlerMethod类型 |
Spring MVC为什么要搞这么多HandlerMapping和HandlerAdapter呢
主要还是为了适应不同的场景,静态资源的请求用SimpleUrlHandlerMapping是不是特别方便,逻辑清晰还容易调试。而RequestMappingHandlerMapping则比较适合写业务,因为能适应复杂多变的场景
ViewResolver和View
ViewResolver用来解析视图,View是最终返回给用户的视图。
因为现在企业项目中,前后端都做了分离。因此不再深入介绍这部分内容。
用@RequestMapping来实现handler时,当我们在类上加了@ReponseBody注解时,会直接将返回写入reponse,并且handler返回的ModelAndView为null,这样ViewResolver和View这个流程就不会走到了
文章转载自公众号:Java识堂
