Java进阶之路对标阿里P6(3)——SpringMvc深度学习(8千字)

本文详细介绍了SpringMVC的工作流程,从请求处理到视图渲染,涵盖核心组件如HandlerMapping、HandlerAdapter和ViewResolver。此外,还讨论了SpringMVC的高级技术,包括拦截器、文件上传、异常处理和Flash属性。通过手写MVC模拟SpringMVC流程,并深入源码解析DispatcherServlet的执行路径,最后提出了基于自定义注解和拦截器实现权限控制的作业思考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringMvc笔记

1、SpringMVC的工作流程

1.1 开发过程

  • web.xml配置dispatcherServlet
  • 开发处理请求的controller(Handler)
  • 配置文件中controller扫描,SpringMVC三大件
  • web.xml中配置资源路径

1.2 请求流程

第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
第⼆步: DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成执行器链即处理器对象及处理器拦截
器(如果 有则⽣成)⼀并返回DispatcherServlet
第四步: DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
第五步:处理器适配器执⾏Handler
第六步: Handler执⾏完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView, ModelAndView 是SpringMVC 框架的⼀个
底层对象,包括 Model 和 View
第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第⼗⼀步:前端控制器向⽤户响应结果

1.3 主要组件

1)HandlerMapping(处理器映射器)
HandlerMapping 是⽤来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是
⽅法。⽐如,标注了@RequestMapping的每个⽅法都可以看成是⼀个Handler。 Handler负责具
体实际的请求处理,在请求到达后, HandlerMapping 的作⽤便是找到请求相应的处理器
Handler 和 Interceptor.
2)HandlerAdapter(处理器适配器)

HandlerAdapter 是⼀个适配器。因为 Spring MVC 中 Handler 可以是任意形式的,只要能处理请
求即可。但是把请求交给 Servlet 的时候,由于 Servlet 的⽅法结构都是
doService(HttpServletRequest req,HttpServletResponse resp)形式的,要让固定的 Servlet 处理
⽅法调⽤ Handler 来进⾏处理,便是 HandlerAdapter 的职责。
3)HandlerExceptionResolver
HandlerExceptionResolver ⽤于处理 Handler 产⽣的异常情况。它的作⽤是根据异常设置
ModelAndView,之后交给渲染⽅法进⾏渲染,渲染⽅法会将 ModelAndView 渲染成⻚⾯。
4)ViewResolver
ViewResolver即视图解析器,⽤于将String类型的视图名和Locale解析为View类型的视图,只有⼀
个resolveViewName()⽅法。从⽅法的定义可以看出, Controller层返回的String类型视图名
viewName 最终会在这⾥被解析成为View。 View是⽤来渲染⻚⾯的,也就是说,它会将程序返回
的参数和数据填⼊模板中,⽣成html⽂件。 ViewResolver 在这个过程主要完成两件事情:
ViewResolver 找到渲染所⽤的模板(第⼀件⼤事)和所⽤的技术(第⼆件⼤事,其实也就是找到
视图的类型,如JSP)并填⼊参数。默认情况下, Spring MVC会⾃动为我们配置⼀个
InternalResourceViewResolver,是针对 JSP 类型视图的。

2、SpringMVC高级技术

2.1 拦截器Interceptor

Servlet:处理Request请求和Response响应
过滤器(Filter):对Request请求起到过滤的作⽤,作⽤在Servlet之前,如果配置为/*可以对所
有的资源访问(servlet、 js/css静态资源等)进⾏过滤处理
监听器(Listener):实现了javax.servlet.ServletContextListener 接⼝的服务器端组件,它随
Web应⽤的启动⽽启动,只初始化⼀次,然后会⼀直运⾏监视,随Web应⽤的停⽌⽽销毁
作⽤⼀: 做⼀些初始化⼯作, web应⽤中spring容器启动ContextLoaderListener
作⽤⼆: 监听web中的特定事件,⽐如HttpSession,ServletRequest的创建和销毁;变量的创建、
销毁和修改等。可以在某些动作前后增加处理,实现监控,⽐如统计在线⼈数,利⽤
HttpSessionLisener等。
拦截器(Interceptor):是SpringMVC、 Struts等表现层框架⾃⼰的,不会拦截
jsp/html/css/image的访问等,只会拦截访问的控制器⽅法(Handler)。
从配置的⻆度也能够总结发现: serlvet、 filter、 listener是配置在web.xml中的,⽽interceptor是
配置在表现层框架⾃⼰的配置⽂件中的,如springmvc.xml,在resources目录下
在Handler业务逻辑执⾏之前拦截⼀次
在Handler逻辑执⾏完毕但未跳转⻚⾯之前拦截⼀次
在跳转⻚⾯之后拦截⼀次

2.1.2 拦截器执行流程

CustomInterceptor(preHandle)——HandlerAdapter(Handle)——CustomInterceptor(PostHandler)——DispatcherServlet(render)——CustomInterceptor(afterCompletion)

2.1.3 多拦截器执行流程

对于配置文件的位置,preHandle中顺序执行,postHandle、afterCompletion中则倒序执行。

2.1.3.1 自定义实现
  • 实现HandlerInterceptor接口
  • 配置文件中注册拦截器mvc:Interceptors

2.4 文件上传

  • 配置文件中CommonsMultipartResolver
  • 界面enctype=“multipart/form-data”
  • 代码MultipartFile uploadFile

2.5 异常

  • @ControllerAdvice
  • @ExceptionHandler(ArithmeticException.class)

2.6 flash属性跨重定向请求数据传递

// addFlashAttribute⽅法设置了⼀个flash类型属性,该属性会被暂存到session中,在
跳转到⻚⾯之后该属性销毁
redirectAttributes.addFlashAttribute(“name”,name);

3、手写MVC

流程:Tomcat加载web.xml->前端控制器DispatcherServlet加载指定的springmvc.xml文件->包扫描,扫描注解@Controller,@Service,@RequestMapping,@Autowired->IOC容器进行bean初始化、依赖注入->handlerMapping简历url和Handler的对应关系->等待请求,处理请求

4、SpringMVC源码解析

4.1 DispatcherServlet继承结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P2jUKtiw-1631316685554)(C:\Users\Howey\AppData\Roaming\Typora\typora-user-images\image-20210501040837096.png)]

HttpServlet(doGet/doPost)->HttpServletBean(继承父类,以及一些环境处理)->FrameworkServlet(重写doGet()--processRequest(request, response)调用doService(request, response),其为抽象方法)->DispatcherServlet实现doService(request, response)调用doDispatch(request, response)

4.2 关键调用栈分析

Handler方法的业务代码执行点,为DispatcherServlet中1027行代码doDispatch方法中ha.handle

界面视图渲染代码处(jsp写上java代码,打上断点)处于1044行processDispatchResult。

两者都在doDispatch方法中。SpringMVC的核心处理流程即doDispatch的调用流程。

1)调⽤getHandler()获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截
器)List
但是如何去getHandler的?后⾯进⾏分析
2)调⽤getHandlerAdapter();获取能够执⾏1)中Handler的适配器
但是如何去getHandlerAdapter的?后⾯进⾏分析
3)适配器调⽤Handler执⾏ha.handle(总会返回⼀个ModelAndView对象)
4)调⽤processDispatchResult()⽅法完成视图渲染跳转

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 {
         // 1.检查是否文件上传
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         // 2.获得执行器链,包含Handler和Interceptor
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
         // 3.获得handlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         // 4.获得LastModify
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

         // 5.拦截器的preHandler校验
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.
         // 6.handle执行
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         // 7.拦截器的postHandle
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      // 8.视图渲染
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      // 9.拦截器的afterCompletion
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

4.3 getHandler

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dpF1uQG-1631316685558)(C:\Users\Howey\AppData\Roaming\Typora\typora-user-images\image-20210501044407987.png)]

遍历两个handlerMapping,获得可执行当前请求的执行器链

4.4 getHandlerAdapter

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMrwWxQX-1631316685560)(C:\Users\Howey\AppData\Roaming\Typora\typora-user-images\image-20210501044911141.png)]

有三个adapter,进行遍历

4.5 ha.handle

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZDFvGzYR-1631316685562)(C:\Users\Howey\AppData\Roaming\Typora\typora-user-images\image-20210501051252651.png)]

4.6 视图渲染

4.7 组件初始化

5、作业

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w7snhEHB-1631316685564)(C:\Users\Howey\AppData\Roaming\Typora\typora-user-images\image-20210501040532813.png)]

实现思路:

使用端:

自定义注解@Security,在注解中标注用户名,表明哪些用户有访问权限,实现类似Interceptor。

自定义拦截器接口,自定义拦截器实现拦截器接口

如果用户拥有权限,返回true,否则返回false

根据url中的username,跟注解中的用户列表匹配

把拦截器注入容器,用注解标识

权限情况:

"lisi","zhangsan" 有访问的权限:/demo/query
"wangwu"无访问权限

框架端:

初始化拦截器链,

doDispatch(DoPost)中在执行Handler之前,获得执行器链,

改造handler,增加拦截器列表

根据url,pattern,获得

获得拦截器列表,执行拦截器逻辑

问题:

1、获得执行器链中,传入Handler,进行判断的作用是什么。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

中,传入Handler,进行判断的作用是什么。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值