springmvc工作流程_自己手撸一个 Spring MVC

优质文章,及时送达

Spring MVC的工作流程

在 SpringBoot 之前,几乎所有的 Web 应用都是已 web.xml 为入口的,Spring MVC 也不例外,学习过 Servlet 的应该都理解,Spring MVC 其实就是对 Servlet 接口,Servlet 规范的一种实现。Servlet 提供了五个接口,其中两个接口最为核心,分别是 init 方法和 service 方法。

1. init方法:

init方法是在服务器装入 Servlet 时执行,在 Servlet 的生命周期中,它只执行一次。

如它的名字,它做的就是初始化。

2. service方法:

service方法和客户端的请求相关,每当一个客户端发生一次请求,请求一个 HttpServlet 对象,该对象的 service 方法就会被调用。

Spring MVC 的入口是从 web.xml 中配置的


org.springframework.web.servlet.DispatcherServlet

开始的,也是 DispatcherServlet 完成了对 Servlet 接口的实现。对于Spring MVC的工作流程,在网上随便一搜会搜到如下的概括:

1. 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet。

2. DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。

由此得知,该请求该由哪个Controller来处理。

1. DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller。

2. HandlerAdapter处理器适配器去执行Controller并得到ModelAndView,并层层返回给DispatcherServlet。

3. DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。

4. DispatcherServlet将模型数据填充到视图中。

5. DispatcherServlet将结果响应给用户。

其中,对于 HandlerMapping 和 HandlerAdapter 在阅读源码的过程中可能是不太好理解的。对了,更多 Spring 全家桶的文章我都已经整理好了,关注微信公众号 Java后端 ,回复「666」就能下载了。

HandlerMapping:

HandlerMapping是一个接口,这个接口返回的是一个请求访问时处理器映射器会返回具体的执行链(HandlerExecutionChain),其中包括拦截器和映射器,只是找到并不执行。

执行链这里用到了设计模式中的责任链模式,每一个责任链的负责人只需要把自己的任务处理好就好。

而HandlerMapping为什么是个接口呢,是因为 Spring MVC提供了三种不同的书写处理器映射器的方法,只不过我们最常用的是通过注解,通过@Controller@RequestMapping的方式去写我们的Controller。

HandlerAdapter:

和 HandlerMapping 一样,HandlerAdapter 也是个接口。

HandlerAdapter 的意思的是适配器,用到的也是设计模式中的适配器模式,在 Spring MVC 中针对不同的 Handler 需要不同的适配器,例如对于@RequestMapping类型的 Handler 需要 RequestMappingHandlerAdapter 来处理,适配之后通过调用接口的 handle 方法就可以执行对应的方法了。

代码实现

不管是 Spring 还是 Spring MVC,又或是 Mybatis,Spring Data等等。其实在阅读源码或者自己实现的过程中会发现,这些提高开发效率的,封装型的框架,从头到尾离不开的就是 Java 的反射以及 Java 的动态代理。

结合反射以及AOP的思想,根据上面 Servlet 接口和 Spring mvc 流程的介绍以及平时对 Spring MVC 的使用,即使我们不看 Spring MVC 的源码其实也能把我们经常使用的功能简单的实现了。这里对于HandlerMapping 和 HandlerAdapter 我们也不需要设计的如此复杂,只需要实现我们平时最常用的一种就好。

流程设计

想象一下,我们在开发某个系统,我们写好了我们的 Spring MVC 控制层的代码,但是我们没有引入任何依赖,接下来怎么让我们的代码 Run 起来呢?

1. 第一步当然还是要从web.xml入手,和 Spring MVC 一样,我们需要配置一个我们自己的 DispatcherServlet,这个 Servlet 继承自 HttpServlet。

2. 创建完自己的 Servlet 之后就是重写上面提到过的两个核心方法:

init和service。

在 Servlet 的生命周期中,init 只执行一次,对于我们编写好的代码,我们需要把所有的urlPath以及我们的控制器和控制器内的方法做一个映射,这样每次客户端发起一次请求,调用 service 方法的时候可以通过这个 mapping 映射找到对应它该执行的方法。

(由于功能比较简单另外没有实现 Interceptor 拦截器的功能,所以没有使用 Spring MVC 使用的执行链的形式)

3. 找到方法后就该执行了,但是执行前,方法需要的参数我们还没有填充。

参数分为很多种,有用@RequestParam修饰的基本数据类型,有数组,有Map,有对象等等。

这里 Spring mvc 用到的是设计模式中的策略模式,针对不同类型的参数会有不同的 Resolver。

策略模式的使用场景是对于系统中的多个类或是说多个场景,用来区分它们的只是他们的行为不同,像我们要做的参数的解析,数据源都是 HttpServletRequest 的 Attribute,只是我们对于 Attribute 的处理行为不同,我们需要把它填充到不同类型的参数上而已。

4. 在对 Method 的参数进行填充后,一切准备就绪了,这时候执行 Method 就可以得到相应的返回值了,返回值就是我们需要的视图。

我们常用的返回类型有两种,一种是根据路径直接返回一个指定的视图,另外一种是我们平时在Spring MVC中用@ResponseBody或者@RestController修饰的直接返回给前端一个JSON形式的串。

这里很简单,其实就是根据不同的情况调用 Servlet 给我们提供的方法。

流程图

3f9ea289646e3af97538a86cf09119ad.png

代码结构

bab3ac168bae0c8b6beae1b7b48638b9.png

HandleMapping

HandleMapping 的功能如上面所说,只是根据请求找到我们对应处理请求的 handler。这个类主要有两个函数,第一个是初始化,将代码中所有被 @Controller@RequestMapping修饰的类和方法,以 @RequestMapping的值作为key,以 Method 作为 value,初始化一个map。第二个就是根据 key 在刚才初始化的map中获取对应的 Method。
public class HandleMapping {
private static final Map mappings = new HashMap<>;
public static void init {
Set> controllerSet = ReflectionUtils.getAllClass(Controller.class);
controllerSet.forEach((controller) -> {
RequestMapping requestMappingAnnotation = controller.getAnnotation(RequestMapping.class);
if (requestMappingAnnotation == ) {
throw new DumpException("controller '" + controller.getName + "' must have a '@RequestMapping' annotation");
}
String parentPath = requestMappingAnnotation.value;
Method methods = controller.getMethods;
for (Method method : methods) {
RequestMapping methodRequestMappingAnnotation = method.getAnnotation(RequestMapping.class);
if (methodRequestMappingAnnotation == ) {
continue;
}
String path = methodRequestMappingAnnotation.value;
try {
mappings.put(parentPath + path, new HandlerMethod(controller.newInstance, method));
} catch (Exception e) {
throw new DumpException("init controller failed,can not create instance for controller '" + controller.getName + "'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值