Spring MVC简介
Java Web发展历程
Java Web应用程序的结构经历了Model I和Model II两个时代,从Model I 发展到Model II 是技术发展的必然。
Model I
在早期的Java Web应用开发中,JSP文件即要负责处理业务逻辑和控制程序的运行流程,还要负责数据的显示,即用JSP文件来独立自主地完成系统功能的所有任务。传统的Model I模式,如图所示。
改进的Model I利用JSP页面与JavaBean组件共同协作来完成系统功能的所有任务,JSP文件负责程序的流程控制逻辑和数据显示逻辑任务,JavaBean负责处理业务逻辑任务。改进的Model I模式,如图所示。
Model II
Model II模式是基于MVC架构的设计模式。在Model II模式下,利用JSP页面、Servlet和JavaBean组件分工协作共同完成系统功能的所有任务。其中,JSP负责数据显示逻辑任务,Servlet负责程序流程控制逻辑任务,JavaBean负责处理业务逻辑任务。Model II模式,如图所示。
MVC模式
MVC思想不是哪个语言所特有的设计思想,也并不是Web应用所特有的思想,是一种规范。
MVC思想将一个应用分成三个基本部分:Model(模型)、View(视图)和Controller(控制器),这三个部分以最少的耦合协同工作,从而提高应用的可扩展性和可维护性。MVC设计模式中模型、视图和控制器三者之间的关系,如图所示。
概括起来,MVC模式具有如下特点:
-
各司其职、互不干涉。在MVC模式中,3层各司其职,所有如果哪一层的需求发生了变化,就只需要更改相应层中的代码,而不会影响到其他层。
-
有利于开发中的分工。在MVC模式中,由于按层把系统分开,就能更好地实现开发中的分工。网页设计人员可以开发JSP页面,对业务熟悉的开发人员可以开发模型中相关业务处理的方法,而其他开发人员可开发控制器,以进行程序控制。
-
有利于组件的重用。分层后更有利于组件的重用,如控制层可独立成一个通用的组件,视图层也可做成通用的操作界面。MVC最重要的特点就是把显示和数据分离,这样就增加了各个模块的可重用性。
Spring MVC
Spring MVC是Spring框架中用于Web应用开发的一个模块,是Spring提供的一个基于MVC设计模式的轻量级Web框架。Spring框架提供了构建Web应用程序的全功能MVC模块。Spring MVC框架本质上相当于Servlet,提供了一个DispatcherServlet作为前端控制器来分派请求,同时提供灵活的配置处理程序映射、视图解析、语言环境和主题解析,并支持文件上传。
在MVC设计模式中,Spring MVC作为控制器(Controller)来建立模型与视图的数据交互, 是一个典型的MVC框架,是结构最清晰的MVC Model II实现,如图所示。
Spring MVC分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让他们更容易进行定制。Spring MVC框架无论是在框架设计还是扩展性、灵活性等方面都全面超越了Struts 2等MVC框架,而且它本身就是Spring框架的一部分,与Spring框架的整合可以说是无缝集成,性能方面具有天生的优越性,Spring MVC具有如下特点:
- Spring MVC拥有强大的灵活性、非侵入性和可配置型。
- Spring MVC提供了一个前端控制器DispatcherServlet,开发者无须额外开发控制器对象。
- Spring MVC分工明确,包括控制器、验证器、命令对象、模型对象、处理程序映射器和视图解析器等,每一个功能实现由一个专门的对象负责。
- Spring MVC可以自动绑定用户输入,并正确转换数据类型。
- Spring MVC使用一个名称/值的Map对象实现更加灵活的模型数据类型。
- Spring MVC内置了常见的校验器,可以检验用户输入,如果校验不同,则重定向回输入表单。输入校验是可选的,并且支持编程方式即声明方式。
- Spring MVC支持国际化,支持根据用户区域显式多国语言,并且国际化的配置非常简单。
- Spring MVC支持多种视图技术,最常见的有JSP技术以及其他技术,包括Velocity和FreeMarker。
- Spring MVC提供了一个简单而强大的JSP标签库,支持数据绑定功能,使得编写JSP页面更加容易。
Spring MVC环境搭建
<!--junit包单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC 及 Spring系列包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.24.RELEASE</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.24.RELEASE</version>
</dependency>
<!--数据库驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--SpringMVC需要的前端和Servlet包-->
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<!--jsp-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
1、2依赖我们在《Spring中IOC思想(2)和依赖注入详解》中讲解IOC思想时导入
3、4依赖我们在讲解《Spring中数据库编程》中讲解Spring数据库编程时导入
如果需要使用AOP的话还需要导入4个依赖,可以在我的博客《Spring中AOP思想》中查阅
5、6、7依赖是使用过Spring MVC时需要导入的,因为Spring MVC框架本质上相当于Servlet,提供了一个DispatcherServlet作为前端控制器来分派请求,所以我们需要导入Servlet的依赖;我们有视图界面,所以需要导入jsp依赖;JSTL(JavaServer Pages Standard Tag Library,JSP标准标签库)是一个不断完善的开放源代码的JSP标签库,所以也需要导入
为了防止资源在导出时候出现问题,我们还需要在pom.xml中的<build>
标签中写入
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
至此,Spring MVC的配置完成
代码实现
我们在IDE中创建一个Maven的web项目
我们先需要在web.xml中配置Spring MVC的前端控制器DispatcherServlet,DispatcherServlet是整个Spring MVC框架的核心,他负责接收请求并将其分派给相应的处理器处理
但是在配置DispatcherServlet之前,我们还需要注意web.xml文件的格式,模板为
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
</web-app>
因为约定大于配置的原则,我们可以直接将上面的模板也就是头文件复制到web.xml文件中,在<web-app>
中进行DispatcherServlet的配置
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化参数,配置Spring MVC配置文件的位置及名称-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--表示容器在启动时,立即加载dispatcherServlet-->
<load-on-startup>1</load-on-startup>
</servlet>
我们配置完成DispatcherServlet后,需要将所有的URL请求拦截下来后映射到前端控制器DispatcherServlet中,只要这样DispatcherServlet才能在后续进行请求的分发,所以我们还需要
<!--让Spring MVC的前端控制器拦截所有的请求-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
注意:上面的代码的位置是在<web-app>
标签中
在配置前端控制器时我们使用到了springmvc.xml文件,所以我们还需要创建一个springmvc.xml文件
我们创建Maven的web项目后,自动生成的目录结构如上图所示,我们需要在webapp目录下分别创建java目录和resources目录,在java目录下创建我们的dao、service、pojo等包,在resources目录下创建配置文件
在resources目录下创建springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
直接粘贴配置文件的模板,在这个配置文件中,我们使用到了aop、context和mvc,所以要先引入这3样东西的命名空间,这里我将aop、context和mvc的命名空间进行罗列,使用时直接复制粘贴
aop命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
context命名空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
mvc命名空间
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/spring-mvc.xsd
配置文件的格式为
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/spring-mvc.xsd
">
</beans>
在该配置文件中,我们要做两件事
1.配置处理器映射:我们在将所有的请求拦截下来并映射到DispatcherServlet后,需要DispatcherServlet来将请求分配给处理器进行处理,但是要将请求交给哪个特定的Controller处理?它会咨询一个名为HandlerMapping(处理器映射)的Bean,之后把URL请求指定给一个Controller处理(就像web.xml文件使用<servlet_mapping>
将URL映射到相应的Servlet上)
我们可以根据需求来选择处理器映射
Spring提供了多种处理器映射(HanderMapping)支持,例如:
- org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
- org.springframework.web.servlet.SimpleUrlHandlerMapping
- org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
这里我们选择BeanNameUrlHandlerMapping,若没有明确声明任何处理器映射,Spring会默认使用BeanNameUrlHandlerMapping,即在Spring容器中查找与请求URL同名的Bean,处理相应的Controller
2.配置视图解析器:在配置完成处理器映射后我们就需要配置视图解析器,因为处理请求的最后一件事就是解析输出,这个任务由JSP实现,需要确定由哪个视图进行进行请求结果的输出,DispatcherServlet会查找一个视图解析器,将控制器返回的逻辑视图名称转换成渲染结果的实际视图,Spring提供了多种视图解析器,例如:
- org.springframework.web.servlet.view.InternalResourceViewResolver
- org.springframework.web.servlet.view.ContentNegotiatingViewResolver
3.处理器适配器:在Spring MVC的使用中我们还会使用到处理器适配器,关于处理器适配器的功能,我们后面会讲到。在我们这片博客讲到的例子中,springmvc.xml并没有配置处理器映射和处理器适配器,Spring会使用默认的处理器映射和处理器适配器
在讲解完springmvc.xml中配置的内容后,我们就开始配置,处理器映射和处理器适配器不用配置
我们先需要配置一个和URL同名的Bean
<!--配置一个和URL同名的bean-->
<bean id="/hello" class="com.westos.controller.HelloController"></bean>
接下来配置视图解析器
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
至此springmvc.xml文件完成
在配置和URL同名的bean时我们使用到了一个类HelloController
,接下来我们开始创建这个类
这里要讲解遇到的一个问题,在我创建完成Maven的web项目后,我在webapp目录下创建了java目录,在java目录下创建了com目录,在com目录下创建了westos目录,在westos目录下创建了controller目录,在controller创建HelloController
文件时出现了问题,我在controller目录上点击右键,在新建的东西中没有Java Class,如下图所示
通过上网查阅得知是没有对java目录进行标识,我们在java目录上点击右键
可以看到java目录颜色发生变化
现在我们在controller目录上点击右键可以看到Java Class
我们在controller目录下创建类
package com.westos.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
System.out.println("Hello Spring MVC");
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "第一个SpringMVC程序");
mv.setViewName("/ch06/first.jsp");
return mv;
}
}
HelloController类是一个实现Controller接口的控制器,可以处理单一的请求的动作,handleRequest是接口必须实现的方法,该方法必须返回一个ModelAndView对象,这个对象包含视图名或视图名和模型,这样Spring MVC就可以使用视图解析器对模型数据进行解析
本利返回的模型包含一个名为msg的字符串对象,返回的视图路径为/ch06/first.jsp,因此,请求被转发到ch06路径下的first.jsp界面
所以我们需要创建一个first.jsp界面,在webapp目录下创建ch06目录,在ch06目录下一个叫first.jsp的JSP文件,内容为
<%--
Created by IntelliJ IDEA.
User: 87257
Date: 2019/8/20
Time: 9:41
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
之后我们还需要进行项目的部署,需要在Tomcat中进行相关的配置
配置完成后启动项目
可以看到项目正在启动,等待一会儿,自动弹出浏览器窗口
为什么在自动弹出的窗口中显示的内容是Hello World!,因为URL后面还没有具体的请求,所以会默认跳转到index.jsp界面,我们可以查看以下index.jsp文件
index.jsp文件中的内容和界面显示的一致
我们试着请求以下hello
界面上显示的是HelloController
类中ModelAndView
类的对象mv
所包含的字符串对象"第一个SpringMVC程序"
,我们跳转到first.jsp界面后将mv
中的字符串对象进行打印
查看控制台
控制台输出我们之前定义好的语句
项目测试成功
Spring MVC请求流程
通过前面示例,简单总结Spring MVC的处理流程:
- 当用户发送URL请求
http://localhost:8081/SpringMVC/hello
时,根据web.xml中对于DispatcherServlet的配置,该请求被DispatcherServlet截获,并根据HandlerMapping找到处理相应请求的Controller控制器也就是HelloController - Controller处理完成后,返回ModelAndView对象;
- 该ModelAndView对象告诉DispatcherServlet需要通过哪个视图来进行数据模型的展示,DispatcherServlet根据视图解析器把Controller返回的逻辑视图名渲染成真正的视图并输出,呈现给用户。
接下来深入了解Spring MVC框架的请求处理流程,如图所示。
- 用户通过客户端向服务器发起一个request请求,此请求会被前端控制器(DispatcherServlet)所拦截。
- 前端控制器请求处理器映射器(HandlerMapping)去查找Handler,可以依据XML配置或注解去查找。
- 处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成),并返回给前端控制器。
- 前端控制器请求处理器适配器(HandlerAdapter)去执行相应的Handler(常称为Controller)。
- 处理器适配器会调用并执行Handler处理器,这里的处理器指的是程序中编写Controller类,也被称之为后端控制器。在填充Handler的入参过程中,根据配置,Spring将帮助做一些额外的工作:1.消息转换、2.数据转换、3.数据格式化、4.数据验证
- Controller执行完毕后会返回给处理器适配器一个ModelAndView对象(Spring MVC底层对象),该对象中会包含View视图信息或包含Model数据模型和View视图信息。
- 处理器适配器接收到Controller返回的ModelAndView后,将其返回给前端控制器。
- 前端控制器接收到ModelAndView后,选择一个合适的视图解析器(ViewReslover)对视图进行解析。
- 视图解析器解析后,会根据View视图信息匹配到相应的视图结果,反馈给前端控制器。
- 前端控制器收到View具体视图后,进行视图渲染,将模型数据(在ModelAndView对象中)填充到request域。
- 前端控制器向用户响应结果。