1、处理流程和核心组件
1.1 处理流程
Spring MVC大致的处理流程:
流程简述:
- 用户发送请求至前端控制器DispatcherServlet;
- 前端控制器DispatcherServlet收到请求后调用处理器映射器HandlerMapping;
- 处理器映射器HandlerMapping根据请求的URL找到具体的处理器,生成处理器对象Handler及处理器拦截器HandlerIntercepter(如果有则生成),一并返回给前端控制器DispatcherServlet。
- 前端控制器DispatcherServlet通过处理器适配器HandlerAdapter调用处理器Controller。
- 执行处理器Handler(Controller);
- 处理器Controller执行完后返回ModelAndView;
- 处理器映射器HandlerAdapter将处理器Controller执行返回的结果ModelAndView返回给前端控制器DispatcherServlet;
- 前端控制器DispatcherServlet将ModelAndView传给视图解析器ViewResolver;
- 视图解析器ViewResolver解析后返回具体的视图View;
- 前端控制器DispatcherServlet对视图View进行渲染视图(即:将模型数据填充至视图中);
- 前端控制器DispatcherServlet响应用户。
1.2 核心组件概览
以核心组件为主的简化流程图:
核心组件:
- DispatcherServlet:Spring MVC中的前端控制器(Front Controller),负责接收Request并将Request转发给对应的处理组件;
- HandlerMapping:Spring MVC中完成URL到Controller映射的组件。DispatcherServlet接收Request,然后从HandlerMapping中根据URL查找处理Request的HandlerAdapter;
- Controller:是Spring MVC中负责处理Request的组件,处理Request并返回ModelAndView对象;
- ModelAndView:封装结果视图的组件。视图解析器解析ModelAndView并将视图返回给客户端。
2、主要组件
2.1 HandlerMapping
HandlerMapping是用来查找处理器Handler的。Handler可以是类,也可以是方法,比如标注了RequestMapping的每个方法都可以看成一个Handler。Hanlder负责实际的请求处理,在请求到达之后,HandlerMapping的作用是找到请求相应的处理器Hanlder和Interceptor。
2.2 HandlerAdapter
HandlerAdapter是一个适配器。因为Spring MVC中的Handler可以是任意形式的,但是把请求交给Servlet的时候,由于Servlet的方法结构都是doService(HttpServletRequest request, HttpServletResponse response)形式,要让固定的Servlet处理方法调用Handler来进行处理,这一步工作就是Handler要做的事情。
2.3 HandlerExceptionResolver
HandlerExceptionResolver是用来处理Handler产生的异常情况的组件。组件根据异常设置ModelAndView,然后交给渲染方法进行渲染,渲染方法会将ModelAndView渲染成页面。
需要注意的是,HandlerExceptionHandler只用于解析对请求做处理阶段的异常,不负责渲染阶段的异常。
2.4 ViewResolver
ViewResolver是视图解析器。通常在Spring MVC的配置文件中,都会配上一个实现类来进行视图解析。这个组件主要的作用是将String类型的视图名称和LocaleView类型的视图,只有一个resolveViewName()方法,用于将Controller层返回的String类型的视图名viewName解析成View。
View是用来渲染页面的,它会将程序返回的参数和数据填入模板中,生成HTML文件。ViewResolver在这个过程中主要做两件大事:
- 找到渲染所用的模板;
- 找到渲染所用的技术,即视图的类型,例如JSP,并填入参数。
2.5 RequestToViewNameTranslator
RequestToViewNameTranslator组件的作用是从请求中获取ViewName。因为ViewResolver根据ViewName查找View,但是有的Handler处理完成之后,没有设置View,也没有设置ViewName,就需要通过这个组件来从请求中查找ViewName。
2.6 LocaleResolver
ViewResolver的resolveViewName()方法需要两个参数,一个是视图名,另一个就是Locale。
LocaleResolver用于从请求中解析出Locale,比如在中国,Locale就是zh-CN,用来表示一个区域。
2.7 ThemeResolver
ThemeResolver是用来解析主题的组件。主题就是样式、图片以及它们形成的显示效果的集合。Spring MVC的一套主题对应一个properties文件,里面存放着与当前主题相关的所有资源,如图片、CSS样式等。
创建主题只需要准备好资源,然后创建一个“主题名.properties”并将资源设置进去,放在classpath下,之后就可以在页面中使用了。
Spring MVC中与主题名有关的类:
- ThemeResolver:负责从请求中解析出主题名;
- ThemeSource:负责根据主题名找到具体的主题;
- Theme:主题的抽象,可以通过Theme来获取主题和具体的资源。
2.8 MultipartResolver
MultipartResolver用于处理上传请求,通过将普通请求包装成MultiHttpServletRequest,使普通请求拥有文件上传的功能。MultiHttpServletRequest可以通过getFile()方法直接获得文件。如果上传多个文件,还可以调用getFileMap()方法得到Map<FileName, File>这种结构。
2.9 FlashMapManager
FlashMapManager就是用来管理FlashMap的。
FlashMap用于重定向时的参数传递,比如在处理用户订单时,为了避免重复提交,可以处理完post请求后重定向到一个get请求,这个get请求可以用来显示订单详情之类的信息。这个页面上要显示的订单的信息,可以通过FlashMap来传递。只需要在重定向前将要传递的数据写入到请求的属性OUTPUT_FLASH_MAP_ATTRIBUTE中,这样在重定向之后的Handler中Spring就会自动将其设置到Model中,在显示订单信息的页面上就可以直接从Model中获得数据。
3、处理流程详解
Spring MVC的处理过程可以分为三步:
- ApplicationContext初始化时用Map保存所有URL和Controller类的对应关系;
- 根据请求URL找到对应的Controller,并从Controller中找到处理请求的方法;
- 将Request参数绑定到方法的形参上,执行方法处理请求,并返回结果视图。
3.1 初始化阶段
在DispatcherServlet中调用父类HttpServletBean的init()方法,执行初始化。初始化要做的事情:
- 初始化IoC容器:调用refresh()方法初始化IoC容器;
- 初始化组件:初始化IoC容器之后,又会调用onRefresh()方法,执行九大组件的初始化;
- 建立URL和Controller的映射关系:通过HandlerMapping的子类AbstractDetectingUrlHandlerMapping实现的initApplicationContext()方法实现。方法中会遍历所有的Bean,遍历所有的Controller上的URL和方法上的URL,并存储为Map<String [] urls, String beanName>的映射关系。
3.2 运行阶段
运行调用是由请求触发的,所以入口为DispatcherServlet的核心方法doService(),doService()的核心是由doDispatch()实现的。任务执行流程:
- 检查是否是文件上传的请求;
- 取得处理当前请求的Controller,实际上取到的是封装了Handler(Controller)和Interceptor的HandlerExecutionChain对象;
- 将请求参数映射成Controller中相应方法的入参;
- 根据Handler实例获取HandlerAdapter实例;
- 通过HandlerAdapter实例调用相应的Handler,并返回结果视图对象;
- 结果视图对象处理,并返回处理结果。
对于将请求参数映射成Controller中相应方法的入参,Spring MVC提供了两种参数绑定的方式:
- 通过注解进行绑定:@RequestParam("paramName");
- 通过参数名进行绑定。
由于Java反射只提供了获取方法参数类型的方法,而并没有提供获取参数名的方法,所以Spring MVC使用asm框架读取字节码,来获取方法的参数名。个人建议使用通过参数进行绑定的方式,这样可以省去asm框架读取字节码的操作。