目录
如何伪装成一个服务端开发(七)
MVC模式
关于mvc模式这里就不再详细说明了,不过对应到Spring开发的流程还是可以学习一下的。
视图发出http请求,发送给控制器(注意,这里叫做Controller和我们后面要说的Controller的意义有所不同)。控制器会进行请求的分发,最终到达模型层。然后模型层要么进行服务要么再继续从数据库取数据,然后返回给控制层,控制层再返回给前台视图。
Spring MVC流程
尽管在 Spring Boot 的开发中,我们可以很快速地通过配置实现 Spring MVC 的开发,但为了解决实际的问题,我们还是很有必要了解 Spring MVC 的运行流程和组件,否则很难理解 Spring Boot 自动为我们生成了什么,配置了什么,这些有什么用。流程和组件是 Spring MVC 的核心,Spring MVC 的流程是围绕 DispatcherServlet 而工作的,它负责协调和组织不同组件完成请求处理并返回响应工作。
上图是一张Spring MVC工作的标准流程图,但是在进行这些流程之前,我们还需要经历至关重要的一步,服务器启动。在Spring Boot的环境下,当服务器启动,它就会初始化Spring MVC的一些重要组件,比如DispactherServlet HandlerAdapter等。我们可以在DispatcherServlet.properties 这个文件中找到被初始化的类,并且会存放在Spring IoC容器中。
# 国际化解析器
org.springframework.web.servlet.LocaleResolver= ...
# 主题解析器
org.springframework.web.servlet.ThemeResolver=...
# HandlerMapping 实例
org.springframework.web.servlet.HandlerMapping=...
# 处理器适配器
org.springframework.web.servlet.HandlerAdapter=...
# 处理器异常解析器
org.springframework.web.servlet.HandlerExceptionResolver=...
# 策略视图名称转换器,当你没有返回视图逻辑名称的时候,通过它可以生成默认的视图名称
org.springframework.web.servlet.RequestToViewNameTranslator=...
# 视图解析器
org.springframework.web.servlet.ViewResolver=...
# FlashMap 管理器。不常用,不再讨论
org.springframework.web.servlet.FlashMapManager=...
另外,我们在代码中还会定义一些@Controller ,它表示这是一个控制器,比如我们在上一节定义了一个控制器
@Controller
@RequestMapping("/mybatis")
public class MyBatisController {
@Autowired
private MyBatisUserService myBatisUserService = null;
@RequestMapping("/getUser")
@ResponseBody
public User getUser(Long id) {
return myBatisUserService.getUser(id);
}
@RequestMapping("/test")
@ResponseBody
public String test() {
return "test";
}
}
@RequestMapping代表请求路径和控制器(和它其中的方法)的映射关系。前面看到在Spring MVC初始化的时候有一个HandlerMapping对象,这个对象会扫描所有的@RequestMapping并且保存起来。当用户发起的请求被DispatcherServlet连接后,就会通过HandlerMapping进行匹配。匹配完成后,HandlerMapping会返回一个HandlerExecutionChain对象
public class HandlerExecutionChain {
// 日志
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
// 处理器
private final Object handler;
// 拦截器数组
private HandlerInterceptor[] interceptors;
// 拦截器列表
private List<HandlerInterceptor> interceptorList;
// 拦截器当前下标
private int interceptorIndex = -1;
......
}
其中handler字段就是对@Controller的包装,里面包含了参数处理逻辑,拦截器处理逻辑等。
得到这个匹配返回对象之后我们需要去执行他,担任执行者的就是HandlerAdapter,不同的handler可能有不同的执行逻辑,所以这个时候就会存在多个HandlerAdapter去执行操作。
抛开内部复杂的运行逻辑,从宏观上来看,最终我们的@Controller中的方法得以被执行,最终将返回一个ModelAndView(但是我们在使用的时候可能会看到直接返回string,或者返回json字符的,我们晚点再解释,多数情况最终还是会被封装成ModelAndView)。然后ModelAndView会传入驶入解析器ViewResolver去生成(定位)最后的视图,最终将视图渲染出来。
在Spring Boot的默认初始化中,我们能够看到默认的ViewResolver 的 InternalResourceViewResolver ,我们可以通过application.properties 配置前缀和后缀。
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
通过这个配置InternalResourceViewResolver 这个视图解析器解析时就会添加前缀和后缀。比如控制器返回 "/user/details"就会添加前后缀然后定位视图。
配置你的Spring MVC
一下列出了Spring MVC的可配置项
# SPRING MVC (WebMvcProperties)
spring.mvc.async.request-timeout= # 异步请求超时时间(单位为毫秒)
spring.mvc.contentnegotiation.favor-parameter=false
# 是否使用请求参数(默认参数为"format")来确定请求的媒体类型
spring.mvc.contentnegotiation.favor-path-extension=false
# 是否使用 URL 中的路径扩展来确定请求的媒体类型
spring.mvc.contentnegotiation.media-types.*=
# 设置内容协商向媒体类型映射文件扩展名。例如,YML 文本/YAML
spring.mvc.contentnegotiation.parameter-name= # 当启用 favor-parameter 参数是,自定义参数名
spring.mvc.date-format= # 日期格式配置,如 yyyy-MM-dd
spring.mvc.dispatch-trace-request=false # 是否让FrameworkServlet doService方法支持 TRACE 请求
spring.mvc.dispatch-options-request=true
# 是否启用 FrameworkServlet doService 方法支持OPTIONS 请求
spring.mvc.favicon.enabled=true # spring MVC 的图标是否启用
spring.mvc.formcontent.putfilter.enabled=true
# Servlet 规范要求表格数据可用于 HTTP POST 而不是 HTTP PUT 或 PATCH 请求,这个选项将使得过滤器拦截
HTTP PUT 和 PATCH,且内容类型是 application/x-www-form-urlencoded 的请求,并且将其转换为 POST 请求
spring.mvc.ignore-default-model-on-redirect=true
# 如果配置为 default,那么它将忽略模型重定向的场景
spring.mvc.locale= # 默认国际化选项,默认取 Accept-Language
spring.mvc.locale-resolver=accept-header # 国际化解析器,如果需要固定可以使用 fixed
spring.mvc.log-resolved-exception=false # 是否启用警告日志异常解决
spring.mvc.message-codes-resolver-format= # 消息代码的格式化策略。例如,' prefix_error_code '
spring.mvc.pathmatch.use-registered-suffix-pattern=false
# 是否对 spring.mvc.contentnegotiation.media-types.*注册的扩展采用后缀模式匹配
spring.mvc.pathmatch.use-suffix-pattern=false # 当匹配模式到请求时,是否使用后缀模式匹配(.*)
spring.mvc.servlet.load-on-startup=-1 # 启用 Spring Web 服务 Serlvet 的优先顺序配置
spring.mvc.static-path-pattern=/** # 指定静态资源路径
spring.mvc.throw-exception-if-no-handler-found=false
# 如果请求找不到处理器,是否抛出 NoHandlerFoundException 异常
spring.mvc.view.prefix= # Spring MVC 视图前缀
spring.mvc.view.suffix= # Spring MVC 视图后缀
我们可以在application.properties中直接配置。这是通过Spring Boot 提供的WebMvcAutoConfiguration类实现的,这个类中有一个静态内部类WebMvcAutoConfigurationAdapter,它继承了WebMvcConfigurer。
而 WebMvcConfigurer 正是Spring MVC用于配置的接口,在没有Spring Boot的情况下,我们会重写这个接口(接口中有很多default方法,所以不必重写所有方法),添加我们需要的拦截器等即可。但是在Spring Boot中,由于自动配置的存在,我们的配置会更简单一些。
中场总结
所以Spring MVC的主要工作就是拦截http请求,并且将根据一定规则将请求分发至对应的方法处理。实际上Spring MVC还有非常多的东西可以学习,但是基本上所有内容都是紧紧围绕这个目标进行的。
Spring MVC还能做那些事情?
1.处理器映射。 我们能够制定拦截POST 或者GET等请求方式,限定请求参数,限定请求头等,具体可以学习@RequestMapping 和 @GetMapping等注解
2.参数获取。Spring MVC是如何从请求中获取数据的并且转换成@Controller中的参数的
3.自定义参数转换规则。Spring MVC默认提供了一些转换规则,但是并不能满足所有情况,很多时候需要我们自己定义转换的规则.
4.Spring MVC 将参数转换出来之后提供了参数合法性验证的能力。
5.ModelAndView和ViewResolver学习,默认支持输出pdf,excel文档等能力
6.Spring MVC对文件上传的支持
7.在执行@Controller方法之前之后,完成后还可以设置拦截器。
8.Spring MVC 国际化(语言切换)使用
9.Spring MVC 操作Session
10.Spring MVC 获取请求头
11.@ResponseBody
12.重定向
13.@Controller 的运行通知
上面列出的这些只是想总结一下Spring MVC还能够做那些东西,我们并不着急一次学全,在理解Spring MVC运行逻辑的情况下,其他知识内容我们可以在需要的时候慢慢学习补充。