这样解释SpringMVC你理解了吗?

2 篇文章 0 订阅

SpringMVC执行流程

1.先解释一下SpringMVC的执行流程,相信网上有很多,但是图形化还不够细致,我们都知道,Springmvc的执行流程第一步肯定是前端页面发送请求到Springmvc那么Springmvc的第一步就是最最最最核心的DispatcherServlet类了,那么这个类是用来做什么的呢?其实全部的流程基本都是围绕它来展开的。
在这里插入图片描述
首先我们的目的肯定是要找到对应的Controller对吧,那么问题来了,在SpringMVC的执行流当中,谁是Controller???答案是handler!!!
在这里插入图片描述
但是呢,DispatcherServlet是通过什么方式来找到对应的handler呢??找到了又是如何进行操作的呢?可能很多人理所当然的会想,找到controller肯定就走业务啦,然后就返回啦。

当然不是咯,首先我们在执行流程图里肯定会看到有一个叫HandlerMapping的东西,它其实是一个接口,DispatcherServlet就是通过它来找到的handler但是这个接口内定义的接口返回的可不是handler哦,而是HandlerExecutionChain,一个执行的链条,神奇吧,其实在这个执行链条的里面,也就是实现类当中,有一个方法叫做getHandler();这个方法返回的才是我们要的handler,这是为啥呢,为啥要多此一举呢,为啥要返回一个执行的链条呢?
在这里插入图片描述
当然是为了加入我们的拦截器咯,如果是按照以往的方式,那么肯定是通过springAOP啊或者spring的动态代理啊,来实现拦截的,但是按这个场景来说的话,能否用代理来实现拦截呢?答案是不能的,为什么呢?因为这个Handler是一个Object,它并没有统一的接口,并没有统一的抽象类,明白吧?你一个Object你怎么对它进行动态代理呢?本身动态代理就是去抽象一个Object哦。
所以在这里我们就要引入一个执行链条了,而且可以跟大家说,这种执行链条并不是一个,而是有可能存在多个,类似管道的方式,一个接一个,最后才是我们的Handler,这就是为什么我们的HandlerMapping为什么不会直接返回Handler而是返回一个执行链条。执行链条内的拦截我们可以往它的前面加也可以往后面加。
后面追加的代码是通过applyAfterConcurrentHandlerStarted(HttpServletRequest request,HttpServletResponse response);方式来进行的。
也可以在它处理的时候添加执行链:applyPostHandler,还可以在前面加,请看源码方法名就可以知道

执行链条上就是一个执行器连着一个执行器的。
大概是这么个样子。。。这就是一个执行链条,注意!!,handler之后也是可以添加执行器的哦。有点像pipeline,后面会重点解释
在这里插入图片描述

在这里插入图片描述
关系图当中的:AbstractDetectingUrlHandlerMapping是用来自动发现的,如果你不需要自动发现的话,这个时候会通过SimpleUrlHandlerMapping,来执行,就由我们自己来配置Url与Controller之间的映射关系,所有的url通过handlerMapping与我们的Controller进行一对一的匹配进行存储都是存储在父类,也就是AbstractUrlHandlerMapping当中。

左边呢,就是都通过URL与Controller来进行配置的,区别就在于一个用于自动发现,一个需要手动配置,而手动配置的话就是在spring-mvc.xml当中进行bean的配置而已。

默认springmvc会在它的核心包当中的一个配置文件去加载beanNameUrlHandlerMapping与AnnotationUrlHandlerMapping,这两个配置是在DispatcherServlet.propertites文件当中

右边呢,就是通过方法来对应handler,也就是对应到一个controller,之前是通过对象的形式进行映射,这边就是通过方法的形式进行映射

RequestMappingInfoHandlerMapping:看上去是不是贼熟悉???这也是我们平时写代码时候用的最多的注解,看到EmptyHandlerMappting了吗?这是一个空的mapping一般情况下我们不需要去它

但是当DispacherServlet当中没有url的mapping也没有method的mapping的时候它会默认构造一个emptyhandlerMapping不会反悔空的

下面我们来配置一个simpleUrlMapping演示一下

在这里插入图片描述
看到图中有两个controller了吧,那么这个时候我们配置了自定义的mapping的时候,还是否能够访问之前的controller了呢?

答案是失败的不可以的。

因为只要你配置了simpleHandlerMapping之后,默认的beanNameUrlHandlerMapping就会失效的

所以只能找到hello.do2,找不到hello.do了。

除非你在配置文件里面再配置一个beanNameUrlHandlerMapping的话就可以了。
在这里插入图片描述
如果是多种配置方式,就会全部生效

所以DispatcherServlet与我们的handlerMapping是一个一对多的关系。

而url体系的handlermapping会根据AbstractHandlerMapping当中的getOrder();方法中的order值来找对应的mapping,你的order值越高mapping的优先级就越高越先被查找,找不到的再向下找

如果你上面叫hello.do下面也叫hello.do那就根据order值来进行匹配。关于handlermapping就到这里了。

最后总结一下:
1.beanNameUrlHandlerMapping:对应的就是以"/"开头的bean的name的URL,这是IOC当中设定的,自动发现就是基于bean的名称来自动发现的。
2.simpleUrlHandlerMapping:对应的就是自定义的bean
3.RequestMapping:就是通过注解@requestMapping咯。

SimpleUrlHandlerMapping的初始化流程:setUrlMap(Map<String,?>urlmap)—initApplicationContext()当中的registerHandlers(this.urlMap);

org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#setUrlMap
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#initApplicationContext
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#registerHandlers
// /表示根路径 /* 表示默认路径
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler()

获取 Handler流程关键源码:

org.springframework.web.servlet.DispatcherServlet#doService
org.springframework.web.servlet.DispatcherServlet#doDispatch
org.springframework.web.servlet.DispatcherServlet#getHandler
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal
// 获取URL路径
org.springframework.web.util.UrlPathHelper#getPathWithinApplication
// 查找handler
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler
// 封装执行链
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain

获取到相对应的handler后呢该干啥了??这时候获取到handler后其实还没有完,因为DispatcherServlet并不知道应该走哪个业务处理,这个时候就要走到一个步骤了也就是通过handler找到相对应的HandlerAdapter

因为在SpringMVC当中,它是有4种方式来实现handler的,分别是Controller,HttpServlet,HttpRequestHandler,以及@RequelstMapping

在这里插入图片描述

HandlerAdapter详解
这里spring mvc 采用适配器模式来适配调用指定Handler,根据Handler的不同种类采用不同的Adapter,其Handler与 HandlerAdapter 对应关系如下:
在这里插入图片描述
HandlerAdapter 接口方法

在这里插入图片描述

HandlerAdapter 接口结构图
在这里插入图片描述
在这里插入图片描述

上面就是自定义配置HandlerAdapter的演示了,SpringMVC默认会使用图中这三种

在这里插入图片描述
上述例子中当IOC 中实例化这些类之后 DispatcherServlet 就会通过
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter() 方法查找对应handler的适配器 ,如果找不到就会报 如下异常 。
javax.servlet.ServletException: No adapter for handler [com.tuling.control.SimpleControl@3c06b5d5]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1198)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)

那么有了对应adpater了,就会走相对应的业务代码了,但是没完,后面还有视图处理的部分

我们都知道视图处理会 通过handler传入到ViewResolver当中,而ViewResolver接口当中只有一个方法:View resolveViewName(String viewName, Locale locale) throws Exception;
在方法中我们能看出,它返回了一个View,而如何返回View就是通过传入的View来寻找

org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个 Could not resolve view with name 异常。

在这里插入图片描述

我们可以通过自定义View来进行视图解析,常用的默认解析就是InternalResourceViewResolver

在这里插入图片描述
首先创建一个类来实现View接口,然后在springmvc当中配置成bean,再加入相对应的viewResolver就可以了。

这样的话业务处理后就可以通过ViewName找到该View视图解析,就可以返回给前端了。

在这里插入图片描述
代码中配置了多种ViewResolver,也可以配置常用的InternalViewResolver

在这里插入图片描述
在这里插入图片描述

这样SpringMVC基本就除了RequestMapping注解以及拦截器就基本都解除了,第一次写科普类博客,有错误的地方请斧正!!!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值