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));