Spring MVC
MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
网上有很多人说 MVC 不是设计模式,只是软件设计规范,我个人更倾向于 MVC 同样是众多设计模式中的一种。**java-design-patterns** 项目中就有关于 MVC 的相关介绍。
想要真正理解 Spring MVC,我们先来看看 Model 1 和 Model 2 这两个没有 Spring MVC 的时代。
Model 1 时代
很多学 Java 后端比较晚的朋友可能并没有接触过 Model 1 时代下的 JavaWeb 应用开发。在 Model1 模式下,整个 Web 应用几乎全部用 JSP 页面组成,只用少量的 JavaBean 来处理数据库连接、访问等操作。
这个模式下 JSP 即是控制层(Controller)又是表现层(View)。显而易见,这种模式存在很多问题。比如控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;再比如前端和后端相互依赖,难以进行测试维护并且开发效率极低。
Model 2 时代
学过 Servlet 并做过相关 Demo 的朋友应该了解“Java Bean(Model)+ JSP(View)+Servlet(Controller) ”这种开发模式,这就是早期的 JavaWeb MVC 开发模式。
- Model:系统涉及的数据,也就是 dao 和 bean。
- View:展示模型中的数据,只是用来展示。
- Controller:处理用户请求都发送给 ,返回数据给 JSP 并展示给用户。
Model2 模式下还存在很多问题,Model2 的抽象和封装程度还远远不够,使用 Model2 进行开发时不可避免地会重复造轮子,这就大大降低了程序的可维护性和复用性。
于是,很多 JavaWeb 开发相关的 MVC 框架应运而生比如 Struts2,但是 Struts2 比较笨重。
Spring MVC 时代
随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加简单和方便,开发效率更高,并且 Spring MVC 运行速度更快。
MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。
SpringMVC 工作原理
Spring MVC 原理如下图所示:
SpringMVC 工作原理的图解我没有自己画,直接图省事在网上找了一个非常清晰直观的,原出处不明。
流程说明(重要):
- 客户端(浏览器)发送请求,直接请求到
DispatcherServlet
。 DispatcherServlet
根据请求信息调用HandlerMapping
处理器映射器,解析请求对应的Handler
。- 解析到对应的
Handler
(也就是我们平常说的Controller
控制器) - 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给
DispatcherServlet
。 DispatcherServlet
调用HandlerAdapter
处理器适配器。HandlerAdapter
会根据Handler
来调用真正的处理器开处理请求,并处理相应的业务逻辑。- 处理器处理完业务后,会返回一个
ModelAndView
对象,Model
是返回的数据对象,View
是个逻辑上的View
。 HandlerAdapter
将controller
执行结果ModelAndView
返回给DispatcherServlet
。DispatcherServlet
将ModelAndView
传给ViewReslover
视图解析器。ViewResolver
会根据逻辑View
查找实际的View
。DispaterServlet
把返回的Model
传给View
(视图渲染)。- 把
View
返回给请求者(浏览器)
请求 —> DispatcherServlet
(前端控制器)—> 调用HandlerMapping
(处理器映射器)—> DispatcherServlet
调用 HandlerAdapter
(处理器适配器)—> 适配调用具体的Controller
—> 返回ModelAndView
—> 传给ViewReslover
视图解析器 —> 解析后返回具体View
—> 根据View
进行渲染视图响应用户.
Spring与SpringMVC父子容器的区别和联系
- Spring 与SpringMVC 两个都是容器,存在父子关系(包含和被包含的关系)
- Spring容器中存放着mapper代理对象,service对象,SpringMVC存放着Controller对象。子容器SpringMVC中可以访问父容器中的对象。但父容器Spring不能访问子容器SpringMVC的对象(存在领域作用域的原因,子容器可以访问父容器中的成员,而子容器的成员则只能被自己使用)。如:Service对象可以在Controller层中注入,反之则不行。
- Spring容器导入的properties配置文件,只能在Spring容器中用而在SpringMVC容器中不能读取到。 需要在SpringMVC 的配置文件中重新进行导入properties文件,并且同样在父容器Spring中不能被使用,导入后使用@Value(“${key}”)在java类中进行读取。
SpringMVC拦截器
常见应用场景
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等
- 权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面
- 性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录)
- 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
- OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。
拦截器接口
1 | public interface HandlerInterceptor { |
拦截器和过滤器什么区别
Spring的拦截器与Servlet的过滤器Filter有很多相似之处,比如两者都是AOP编程思想的体现,都能实现权限检查、日志记录等,不同的是:
- 使用范围不同:Filter是Servlet规范规定的,只能用于Web程序中,而拦截器既可以用于Web程序,也可以用于Application、Swing程序中
- 规范不同:Filter是Servlet规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的
- 使用的资源不同:拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IoC注入到拦截器即可,而Filter则不能
- 深度不同:Filter只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring架构的程序中,要优先使用拦截器。
- 实现原理不同:拦截器是基于动态代理来实现的,而过滤器是基于函数回调来实现的。
- 作用域不同:拦截器只对Action起作用,过滤器可以对所有请求起作用。
- 调用次序不同:在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。