在 B/S 架构中,系统标准的三层架构包括:表现层、业务层、持久层。
![162ea43f050b5dde910d15c9b2a92c5b.png](https://i-blog.csdnimg.cn/blog_migrate/63af6d5d946d6aece78734839370844c.png)
表现层:也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用 http协议请求web层,web 需要接收 http 请求,完成 http 响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
业务层:也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。
持久层:也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体
MVC 是表现层的设计模型,和其他层没有关系
![f15e32b1312533845eaaef6fdbb8ce0d.png](https://i-blog.csdnimg.cn/blog_migrate/d335135024889dccb1f5e4ba62299262.png)
- Model(模型):模型代表一个存取数据的对象或 JAVA POJO。
- View(视图):视图代表模型包含的数据的可视化。
- Controller(控制器):控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
表现层一般采用MVC模式,但不代表表现层都采用MVC模式,比如MVVM模式。
它全称是 Model View VielModel。是针对 mvc 模型的再次改良,不过只改良了展示数据的部分。(Controller 的再次优化交给了框架, Model 部分已经无需再优化)
MVC中,javascript 的逻辑处理和数据显示交织在了一起,当我们想进行调整时,需要阅读大量的代码,给后期维护造成了影响。而 MVVM 它把 javascript的逻辑处理和数据展示分开,可以让使用者在后期维护时,针对不同的需求进行调整。
例如:如果是逻辑部分需要处理,则修改逻辑部分代码。如果是数据显示位置需要调整,则修改展示部分的代码,使前端展示更加灵活,也更加合理。
前端框架中vue就是MVVM模式。
SpringMVC的执行流程
![34ec5434bc6310766fbbbb77bca27b89.png](https://i-blog.csdnimg.cn/blog_migrate/cacab7f1d239b79c41e90d0eeef627b1.jpeg)
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用处理器映射器HandlerMapping。
- 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
- DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
- 执行处理器Handler(Controller,也叫页面控制器)。
- Handler执行完成返回ModelAndView
- HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View
- DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
- DispatcherServlet响应用户。
SpringMVC中的组件说明:
![89c42be17334420f42dbbef366cfa56e.png](https://i-blog.csdnimg.cn/blog_migrate/0bbf894f3a072466ffe1b7176d2e06bf.jpeg)
- DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性,系统扩展性提高。SpringMVC中的DispatcherServlet由框架实现。
- HandlerMapping:处理器映射器。HandlerMapping负责根据用户请求的url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,根据一定的规则去查找,例如:xml配置方式,实现接口方式,注解方式等。SpringMVC中的HandlerMapping由框架实现。
- Handler:处理器。Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
- HandlAdapter:处理器适配器。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。SpringMVC中的HandlAdapter由框架实现。
- ModelAndView:是springmvc的封装对象,将model和view封装在一起。
- ViewResolver:视图解析器。ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
- View:是springmvc的封装对象,是一个接口, springmvc框架提供了很多的View视图类型,包括:jspview,pdfview,jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
SpringMVC中的三大组件
处理器映射器
它指的是: RequestMappingHandlerMapping
它起的作用是为我们建立起 @RequestMapping 注解和控制器方法的对应关系。并且存在于MappingRegistry 对象中的 mappingLookup 的 Map 中,该 Map 是个 LinkedHashMap。
对应关系的建立时机是在应用加载的时候,也就是当服务器启动完成后,这些对应关系已经建立完成了。从而做到在我们访问的时候,只是从 Map 中获取对应的类和方法的信息,并调用执行。
![f6d3ee6a9d3119ec3ff8b9dda2a6c571.png](https://i-blog.csdnimg.cn/blog_migrate/1df912ee724e468e7b7f710b2b83caff.jpeg)
处理器适配器
HandlerAdapter
SpringMVC 的执行过程中,最终调用的是前端控制器 DispatcherServlet 的doDispatch 方法,而该方法中的 HandlerAdapter 的 handle 方法实际调用了我们自己写的控制器方法。而我们写的控制方法名称各不一样,它是通过 handle 方法反射调用的。
SpringMVC中有三种适配器:
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
使用此适配器,适用的控制器写法: 要求实现 Controller 接口
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
使用此适配器的控制器写法: 要求实现 HttpRequestHandler 接口
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
这种方式也是我们实际开发中采用最多的。它的要求是我们用注解@Controller 配置控制器
视图解析器
视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。
为了实现视图模型和具体实现技术的解耦, Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口。
我们的视图是无状态的,所以他们不会有线程安全的问题。无状态是指对于每一个请求,都会创建一个 View对象。
在 SpringMVC 中常用的视图类型:
![a5de20988bdca67a086d2bc9655c5ebf.png](https://i-blog.csdnimg.cn/blog_migrate/73a390b809c104aaa30af7909e18b168.jpeg)
View Resolver 负责将处理结果生成 View 视图, View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 视图对象是由视图解析器负责实例化。
视图解析器的作用是将逻辑视图转为物理视图,所有的视图解析器都必须实现 ViewResolver 接口
SpringMVC 为不同逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。程序员可以选择一种视图解析器或混用多种视图解析器。可以通过 order 属性指定解析器的优先顺序, order 越小优先级越高, SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则抛出 ServletException异常。
![e68e9db0c2f2673191bbb737265ff092.png](https://i-blog.csdnimg.cn/blog_migrate/94427e5708caf31727625f7679dbb45d.jpeg)
不需要视图解析器的场景分析
以注解@Controller 配置控制器为例,控制器的方法返回值其实支持三种方式:
第一种: String 类型。借助视图解析器,可以在指定位置为我们找到指定扩展名的视图。视图可以是 JSP,HTML 或者其他的控制器方法上的 RequestMapping 映射地址。前往指定视图的方式,默认是请求转发,可以通过redirect:前缀控制其使用重定向。
第二种: void,即没有返回值。因为我们在控制器方法的参数中可以直接使用原始 SerlvetAPI 对象HttpServletRequest 和 HttpServletResponse 对象,所以无论是转发还是重定向都可以轻松实现,而无需使用返回值。
第三种: ModelAndView 类型。其实我们跟踪源码可以发现在 DispatcherServlet 中的 doDispatch 方法执行时, HandlerAdapter 的 handle 方法的返回值就是 ModelAndView,只有我们的控制器方法定义为 void时,才不会返回此类型。当返回值是 String 的时候也会创建 ModelAndView 并返回。
通过上面三种控制器方法返回值,我们可以再深入的剖析一下我们请求之后接收响应的方式,其实无外乎就三种。
第一种:请求转发
第二种:重定向
第三种:直接使用 Response 对象获取流对象输入,可以是字节流也可以是字符流。
其中第一种和第二种的请求转发和重定向的共同点是都会引发页面的跳转。
在我们的实际开发中,如果我们不需要页面跳转,即基于 ajax 的异步请求,用 json 数据交互时,即可不配置任何视图解析器。前后端交互是通过 json 数据的,利用@RequestBody 和@ResponseBody 实现数据到 java对象的绑定。
三个常用注解的使用场景
RequestParam
在请求体的 MIME 类型为 application/x-www-form-urlencoded 或者 application/json 的情况下,无论 get/post/put/delete 请求方式, 参数的体现形式都是 key=value。
SpringMVC 是使用我们控制器方法的形参作为参数名称,再使用 request 的getParameterValues 方法获取的参数。所以才会有请求参数的 key 必须和方法形参变量名称保持一致的要求。但是如果形参变量名称和请求参数的 key 不一致呢? 此时,参数将无法封装成功。
此时 RequestParam 注解就会起到作用,它会把该注解 value 属性的值作为请求参数的 key 来获取请求参数的值,并传递给控制器方法。
/**
RequestBody
SpringMVC 在封装请求参数的时候,默认只会获取参数的值,而不会把参数名称一同获取出来,这在我们使用表单提交的时候没有任何问题。因为我们的表单提交,请求参数是key=value 的。但是当我们使用 ajax 进行提交时,请求参数可能是 json 格式的: {key:value},在此种情况下,要想实现封装以我们前面的内容是无法实现的。 此时需要我们使用@RequestBody 注解。
/**
PathVariable
它是 SpringMVC 在 3.0 之后新加入的一个注解,是 SpringMVC 支持 Restful 风格 URL 的一个重要标志。
/**
部分内容参考
https://www.jianshu.com/p/8a20c547e245
https://www.runoob.com/design-pattern/mvc-pattern.html