六、Spring MVC

目录

 

1. Spring MVC使用示例

2. 主要结构

3. RequestMappingHandlerMapping的实例化和初始化

3.1 RequestMappingHandlerMapping的实例化

3.2 RequestMappingHandlerMapping的初始化(web方法收集)

4. 拦截器

4.1 拦截器使用示例

4.2 拦截器的收集

5. RequestMappingHandlerAdapter

5.1 RequestMappingHandlerAdapter的实例化

5.2 RequestMappingHandlerAdapter的初始化

6. ExceptionHandlerExceptionResolver

6.1 ExceptionHandlerExceptionResolver的实例化

6.2    ExceptionHandlerExceptionResolver的初始化

7. Spring mvc容器中的bean注入

8. 请求的处理流程

8.1 getHandler获取请求路径对应的HandlerMethod和拦截器

8.2 getHandlerAdapter获取HandlerAdapter

8.3 mappedHandler.applyPreHandle正序调用拦截器前置处理方法

8.4 ha.handle调用请求路径对应的web方法

8.4.1 参数解析

8.4.2 反射调用web方法

8.4.3 返回值解析

8.5 mappedHandler.applyPostHandle倒序调用拦截器的中置处理方法

8.6 processDispatchResult

8.6.1 ControllerAdvice异常处理方法调用

8.6.2 倒序调用拦截器后置处理方法


(本篇中用到的演示项目地址:https://gitee.com/yejuan/spring-learning-no-xml 对应tag: spring-c6)

1. Spring MVC使用示例

我们通过类与方法上面的RequestMapping拼接出来的路径/user/showUser就可以调用到showUser方法,本篇将分析下是如何实现的。

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService userService;
    @ResponseBody
    @RequestMapping("/showUser")
    public String showUser(){
        LogUtil.getLogger().info("invoke showUser");
        User user = new User();
        user.setName("ZhangSan");
        user.setAge(18);

        return userService.showUser(user);
    }

}

2. 主要结构

DispatcherServlet中有三个重要的容器handlerMappings、handlerAdapters、handlerExceptionResolvers三个容器中RequestMappingHandlerMapping、RequestMappingHandlerAdapter、ExceptionHandlerExceptionResolver三个实例对web请求的处理尤为重要:

  1. RequestMappingHandlerMapping:DispatcherServlet是我们配置的用来接收所有请求的Servlet, Servlet接收到用户请求后根据请求路径在RequestMappingHandlerMapping 中mappingRegistry的urlLookup匹配到RequestMappingInfo,再在mappingRegistry的mappingLookup中根据RequestMappingInfo找到对应的HandlerMethod,这样就找到了请求路径对应的具体方法,再进行反射调用就可以处理请求了;
  2. RequestMappingHandlerAdapter:通过RequestMappingHandlerAdapter的参数解析器和返回值解析器完成请求的参数解析和返回值解析;
  3. ExceptionHandlerExceptionResolver:可通过ControllerAdvice注解自定义Controller调用发生异常后的增强,ControllerAdvice类型bean的收集和调用通过ExceptionHandlerExceptionResolver支持。

3. RequestMappingHandlerMapping的实例化和初始化

AbstractApplicationContext#refresh方法中的finishRefresh()(调用发生在spring容器bean实例化之后)会通过Listener监听机制调用到DispatcherServlet#onRefresh方法进行初始化。
org.springframework.web.servlet.DispatcherServlet#onRefresh

3.1 RequestMappingHandlerMapping的实例化

默认会读取DispatcherServlet.properties中的配置的key为org.springframework.web.servlet.HandlerMapping对应的value,value为HandlerMapping接口实现类的全限定名,创建相应的beanDefinition再通过Spring那套实例化、初始化流程拿到对应的实例。

默认读取DispatcherServlet.properties配置文件

拿到实现类后,封装生成beanDefinition调用spring那套进行实例化、初始化

org.springframework.web.servlet.DispatcherServlet#getDefaultStrategies

3.2 RequestMappingHandlerMapping的初始化(web方法收集)

RequestMappingHandlerMapping实现了InitializingBean接口在初始化过程中会调用到afterPropertiesSet方法进行web方法(web请求路径对应的方法)的收集。org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet

过滤类上有 Controller、RequestMapping注解的类

遍历方法如果方法上有RequestMapping注解则该方法为web方法结合类上的RequestMapping注解信息包装生成RequestMappingInfo对象, RequestMappingInfo对象中有web方法对应的请求路径与方法,建立web方法方法与RequestMappingInfo映射关系缓存到容器。org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods

  1. 将RequestMappingInfo与HandlerMethod的映射关系加入mappingRegistry的mappingLookup容器,HandlerMethod包装有web方法对应的method、beanName、beanFactory方便反射调用;
  2. 将请求路径字符串与RequestMappingInfo的映射关系加入mappingRegistry的urlLookup容器

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register

到这里我们就完成了web方法的收集和缓存,为处理web请求做好准备。

4. 拦截器

4.1 拦截器使用示例

实现HandlerInterceptor接口定义自己的拦截器

public class MyHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("-------MyHandlerInterceptor   preHandle ------------");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("-------MyHandlerInterceptor   postHandle ------------");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("-------MyHandlerInterceptor   afterCompletion ------------");
    }
}

配置拦截器,配置拦截器的拦截路径,通过@Bean将拦截器加入Spring容器

4.2 拦截器的收集

由2中的结构图可知RequestMappingHandlerMapping的adaptedInterceptors容器中缓存了所有的拦截器,下面我们来分析整理下拦截器的收集过程。spring在初始化RequestMappingHandlerMapping时会调用到RequestMappingHandlerMapping的initApplicationContext进行拦截器的收集。
org.springframework.web.servlet.handler.AbstractHandlerMapping#initApplicationContext

5. RequestMappingHandlerAdapter

RequestMappingHandlerAdapter主要是完成了web方法调用的参数解析和返回值解析

5.1 RequestMappingHandlerAdapter的实例化

同HandlerMapping的实例化一样,RequestMappingHandlerAdapter的实例化也是在AbstractApplicationContext#refresh方法中的finishRefresh()调用到的DispatcherServlet#onRefresh方法中进行的

org.springframework.web.servlet.DispatcherServlet#onRefresh

默认读取DispatcherServlet.properties中的key为org.springframework.web.servlet.HandlerAdapter的配置进行实例化、初始化。

org.springframework.web.servlet.DispatcherServlet#getDefaultStrategies

5.2 RequestMappingHandlerAdapter的初始化

RequestMappingHandlerAdapter实现了InitializingBean接口在初始化过程中会调用到afterPropertiesSet方法,afterPropertiesSet方法中完成了参数解析器与返回值解析器的创建和添加。

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet

参数解析器的创建

返回值解析器的创建

6. ExceptionHandlerExceptionResolver

6.1 ExceptionHandlerExceptionResolver的实例化

ExceptionHandlerExceptionResolver的实例化也是通过DispatcherServlet#onRefresh方法完成的,默认的HandlerExceptionResolver同样的配置在DispatcherServlet.properties文件中。

6.2    ExceptionHandlerExceptionResolver的初始化

ExceptionHandlerExceptionResolver的初始化方法afterPropertiesSet完成了有ControllerAdvice注解bean的收集、解析,以及ControllerAdvice类型bean的参数解析器、返回值解析器的创建和设置。

一个ControllerAdviceBean对应一个ExceptionHandlerMethodResolver对象,ControllerAdviceBean与ExceptionHandlerMethodResolver的映射关系缓存在exceptionHandlerAdviceCache容器中,ExceptionHandlerMethodResolver中的mappedMethods容器缓存了异常类型与异常处理方法的映射关系。

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache

7. Spring mvc容器中的bean注入

在spring源码分析的第二篇中我们分析了spring web项目中有两个容器一个专门装载Controller类型的bean为了区分我们把它称为Spring mvc容器,另一个Spring mvc的parent 容器即root 容器,专门缓存Controller之外的其他bean。Controller bean与其他类型的bean不在同一个容器,Controller中的依赖注入是如何完成的呢,我们来分析下。

         前面讲过AutowiredAnnotationBeanPostProcessor#postProcessProperties会对bean中有@Autowired的属性调用getBean获取实例,进行属性的注入。getBean获取实例时如果父容器不为空并且当前容器(子容器)中没有相关的bean,调用父容器doGetBean获取相关实例。

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

8. 请求的处理流程

8.1 getHandler获取请求路径对应的HandlerMethod和拦截器

根据请求路径从urlLookup拿到RequestMappingInfo,根据RequestMappingInfo从mappingLookup拿到HandlerMethod,从List<HandlerInterceptor> adaptedInterceptors容器中过滤筛选与请求路径匹配的拦截器,包装拦截器与HandlerMethod得到HandlerExecutionChain

org.springframework.web.servlet.DispatcherServlet#getHandler

8.2 getHandlerAdapter获取HandlerAdapter

应用策略模式根据HandlerMethod获取到匹配的HandlerAdapter为RequestMappingHandlerAdapter

org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

8.3 mappedHandler.applyPreHandle正序调用拦截器前置处理方法

正序调用拦截器的前置处理,如果请求被拦截,直接倒序调用拦截器的后置处理方法后返回

8.4 ha.handle调用请求路径对应的web方法

不同的参数和返回值对应的解析器和消息转换器不同,但整个流程框架大致一样的,我们以下面的代码为例分析web方法的执行过程

@Controller
@RequestMapping("/account")
public class AccountController {

    @Autowired
    AccountService accountService2;

    @ResponseBody
    @RequestMapping("/show")
    public String showUser(@RequestBody String requestBody){
        LogUtil.getLogger().info("requestBody :{}", requestBody);
        return "AccountController show";
    }
}

整体流程如下

8.4.1 参数解析

解析web方法的参数,策略模式通过supportsParameter方法找到能解析参数的解析器,如RequestResponseBodyMethodProcessor#supportsParameter 匹配有RequestBody注解的参数,我们的示例代码匹配的解析器为RequestResponseBodyMethodProcessor。

org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver

解析参数,根据参数类型和请求的contentType找到匹配的消息解析器,从请求的输入流中读取内容进行解析

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument

AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

8.4.2 反射调用web方法

org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

8.4.3 返回值解析

返回值的解析与参数解析的流程相似,同样通过策略模式找到返回值匹配的解析器,再从解析器中找到消息转换器,解析返回值将返回值写入请求对应的输出流

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

匹配返回值解析器

获取返回值匹配的解析器,如RequestResponseBodyMethodProcessor匹配类或方法上有ResponseBody注解的返回值org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsReturnType

 

解析返回值,根据返回值类型找到匹配的消息转换器,示例代码将匹配到StringHttpMessageConverter消息转换器,将返回值写入请求的输出流

org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue

AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)

8.5 mappedHandler.applyPostHandle倒序调用拦截器的中置处理方法

org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle

8.6 processDispatchResult

8.6.1 ControllerAdvice异常处理方法调用

发生异常时会调用到到异常处理器ExceptionHandlerExceptionResolver,通过exceptionHandlerAdviceCache容器遍历所有的ControllerAdvice类型的bean,通过ControllerAdvice注解中配置的basePackages、assignableTypes、annotations判断抛出异常的Controller是否被当前ControllerAdvice类型的bean匹配,如果匹配通过异常类型在mappedMethods容器中找到匹配的方法

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod

获取异常对应的ControllerAdvice类型bean的异常处理方法后,设置参数解析器,设置返回值解析器,调用异常处理方法,将异常和请求路径对应的HandlerMethod作为参数传入,解析异常处理方法的参数反射进行异常处理方法方法调用org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException

8.6.2 倒序调用拦截器后置处理方法

org.springframework.web.servlet.DispatcherServlet#processDispatchResult

到这里就完成了spring mvc主流程的源码分析,截止本篇也就完成了spring 整个源码包括spring ioc、spring aop、spring mvc的简要分析。

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值