一、MVC思想与Spring MVC
MVC是一种软件架构模式
- M:Model,即数据模型,主要是封装和映射数据,对应的是Java Bean实体类
- V:View,即视图,显示数据的页面,html,jsp或者ftl文件
- C:Controller,即控制器,控制数据处理逻辑及页面跳转
MVC的流程大概是
Spring MVC
Spring为了展现层提供的基于MVC模式设计的Web框架,是主流的MVC框架之一,Spring MVC通过注解让Java Bean成为处理请求的控制器,无需实现任何接口,并且支持Rest风格的URL形式,采用松散耦合可插拔的组件结构,比其他MVC框架更具有扩展性和灵活性
重要组件:
- DispatcherServlet:负责接收请求和转发请求
- HandlerMapping:处理器映射器,根据请求查找执行类Handler,并返回Handler给到DispatcherServlet
- HandlerAdapter:处理器适配器,执行DispatcherServlet发来的Handler,并返回视图ModelAndView给DispatcherServlet。
- Handler:执行Handler方法,返回ModelAndView给HandlerAdapter,HandlerAdapter在返回给DispatcherServlet
- ViewResolver:视图解析器,解析DispatcherServlet发来的ModelAndView,返回视图
- View:视图,渲染DispatcherServlet发来的视图,并返回给DispatcherServlet,并由DispatcherServlet返回给客户端展示
二、Spring MVC QuickStart
创建Spring MVC项目
首先创建一个maven项目spring-web-mvc,添加项目依赖
代码语言:javascript复制<properties>
<spring-version>5.3.13</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
</dependencies>
添加 Framework Support,添加Web框架支持,勾选创建web.xml
打开Project Structure,选择Artifacts,首先在WEB-INF下创建一个lib文件夹,然后将右侧的jar包导入进lib文件夹中
配置web.xml
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--DispatchServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<!--
DispatcherServlet是Spring MVC最核心的对象
DispatcherServlet用于拦截Http请求,
并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--applicationContext.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--
在Web应用启动时自动创建Spring IOC容器,
并初始化DispatcherServlet
-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--"/" 代表拦截所有请求,/*拦截所有请求包括jsp页面这些请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
在resources目录下新建applicationContext.xml,配置包扫描及视图解析器
代码语言:javascript复制<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.citi">
</context:component-scan>
<!--配置试图解析器,自动拼接页面地址,自动在jsp页面前增加/WEB-INF/pages/-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
配置Tomcat
新建controller包,创建HelloController控制类
代码语言:javascript复制@Controller
public class HelloController {
// 处理转发请求
@RequestMapping("/hello")
public String hello(){
System.out.println("处理中....");
// 返回页面
//return "/WEB-INF/pages/success.jsp";
// 添加视图解析器后,自动拼接页面地址
return "success";
}
}
@RequestMapping:告诉Spring MVC这方法处理什么请求,其中"/"可以省略,习惯加上会比较好
在WEB-INF文件夹下新建pages文件夹,用来存放jsp文件,新建success.jsp文件
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Success</title>
</head>
<body>
success
</body>
</html>
启动Tomcat,浏览器输入 http://localhost:8080/hello 页面显示success
HelloController运行流程: 1).客户端(浏览器)点击链接发送localhost:8080/hello请求 2).Tomcat收到请求 3).SpringMVC的dispatchServlet前端控制器收到所有请求 4).dispatchServlet查看请求地址和@RequestMapping标注的哪个Controller类的方法相匹配 5).前端控制器知道目标类和方法,利用反射执行目标方法 6).方法执行完成之后会有一个返回值,SpringMVC认为这个返回值就是要去的页面 7).拿到返回值,使用视图解析器拼接得到完整的页面地址 8).前端控制器根据地址转发到具体页面
applicationContext.xml配置文件
在web.xml配置文件中的param-value标签下指定了配置文件位置,Spring MVC有默认的配置文件。
注销配置文件,启动Tomcat
报错"Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]" 默认会找这个配置文件springmvc-servlet.xml 修改Servlet名字为dispatcherServlet,再次启动Tomcat,可以看出默认配置文件名称为DispatcherServlet Bean的名字-servlet.xml
如果想不指定配置文件,就需要在WEB-INF目录下配置一个名字为[Servlet名字-servlet.xml]的配置文件,Spring MVC会在Tomcat容器启动时自动查找这个文件 在WEB-INF下创建一个dispatchServlet-servlet.xml文件,文件内容就是原来resources目录下applicationContext.xml的内容,并将applicationContext.xml删除,再次启动Tomcat
不再报错,页面可以正常访问
"/" 与 "/*" 的区别
web.xml配置文件中url-parttern标签的 "/" 代表拦截所有请求(不包括JSP页面),"/*"拦截所有请求包括jsp页面这些请求,将配置中的“/”改为“/*”,重新启动容器,并访问/hello,出现404报错
控制台报错如下
说明 "/" 拦截的请求不包括jsp页面,"/*" 拦截所有的请求,包括jsp页面
在web目录下增加index.html,将 “/*” 改为 “/”,重启容器,访问index.html
页面无法访问,控制台报错,这是为什么?
首先tomcat文件中conf文件夹下本身就有一个web.xml文件,项目中的web.xml就是继承Tomcat conf文件夹下的web.xml
Tomcat中web.xml相当于是父类,其中配置了DefaultServlet,专门用来处理静态资源的,项目中web.xml是子类,都配置了 "/",相当于子类重写了父类的方法,那么Tomcat中的web.xml中的defualtServlet配置的 "/" 也就失效了,也就无法处理html静态资源了。
其中default配置类 "/"
项目中配置 “/” 不拦截jsp请求是为了放行jsp,将jsp交由tomcat处理,tomcat的web.xml中有一个JspServlet,专门处理*.jsp文件的
“/*” 就是直接拦截所有请求,"/" 是为了迎合Rest风格的URL地址
@RequestMapping注解
Spring MVC 使用@RequestMapping注解标注xxController或者方法可以处理哪些URL请求
- @RequestMapping定义在类上表明提供上层URL地址,这是针对方法上@RequestMapping的URL地址来说的
- @RequestMapping定义在方法上,标注了方法能够处理的具体请求
在controller包下创建一个MappingController,测试@RequestMapping注解
plus:不能两个方法处理同一个请求即不能有两个方法的RequestMapping中value是一样的 严格遵循一个方法处理一个请求
@RequestMapping标注在类上,为当前所有方法所处理的请求前增加前缀
代码语言:javascript复制@Controller
@RequestMapping("/mapping")
public class MappingController {
@RequestMapping("/handle")
public String handle(){
String method = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println(this.getClass().getSimpleName() "类的" method "方法正在执行");
return "success";
}
}
浏览器输入http://localhost:8080/mapping/handle
@RequestMapping其他属性
method:限定请求方式,默认为空,也就是说任何请求方法都可以处理
在MappingController中增加方法
代码语言:javascript复制@RequestMapping(value = "/handle_post_req", method = RequestMethod.POST)
public String handlePostReq(){
String method = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println(this.getClass().getSimpleName() "类的" method "方法正在执行");
return "success";
}
表单形式发送POST请求,在index.jsp页面的body标签中增加form表达代码
代码语言:javascript复制<p>发送POST请求</p>
<form action="/mapping/handle_post_req" method="post">
<button type="submit">发送POST请求</button>
</form>
重新启动Tomcat,点击按钮发送POST请求,成功跳转至success.jsp页面
控制台输出
而在浏览器中输入http://localhost:8080/mapping/handle_post_req, 则会报错,说明请求不支持GET方式
params:指定请求参数
- params是一个数组
- params支持简单的表达式,指出 “!”, “!=”
- params指定请求中必须包含指定名的请求参数
在MappingController类中新增代码
代码语言:javascript复制@RequestMapping(value = "/handle_params", params = {"username"})
public String handleParams(){
String method = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println(this.getClass().getSimpleName() "类的" method "方法正在执行");
return "success";
}
请求不携带params指定的参数,报错404
增加username参数,成功跳转至success页面
!params表示请求中必须不带params参数,修改代码
代码语言:javascript复制@RequestMapping(value = "/handle_params", params = {"!username"})
public String handleParams(){
return "success";
}
重启tomcat,请求http://localhost:8080/mapping/handle_params?username=stark
规定参数值,params={"username=peter"},修改代码,重新启动Tomcat,再次输入http://localhost:8080/mapping/handle_params?username=stark
输入 http://localhost:8080/mapping/handle_params?username=peter
也可以指定非指定值,如params={"username!=peter"},指定多个限制规则,必须同时满足,多个参数之间使用 "," 连接,URL地址多个参数之间使用 "&" 链接,如params={"username="stark",password,!gender}
headers,规定请求头表达式与params一样,也是一个数组
HTTP请求头中User-Agent表示发送请求的浏览器,可以使用headers属性指定User-Agent的值 User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0 User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
增加代码
代码语言:javascript复制@RequestMapping(value = "/handle_headers",headers = {"User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0"})
public String handleHeaders(){
return "success";
}
重启Tomcat,谷歌浏览器中访问
火狐浏览器中访问
@RequestMapping的另外两个属性
- consumes:指定接收内容的类型
- produces:指定浏览器返回的内容类型
三、@RequestMapping的模糊匹配
- ? : 支持匹配一个字符
- * : 支持匹配任意字符
- ** : 支持匹配多层路径
新建一个MatchController,测试@RequestMapping的模糊匹配,以及通配符的使用
代码语言:javascript复制@Controller
public class MatchController {
@RequestMapping("/matc?")
public String matchOne(){
return "success";
}
}
“?”匹配一个字符,重启tomcat,输入http://localhost:8080/match
“?”可以表示任意一个字符,但是匹配超过1个字符会报错
“*” 可以匹配多个字符
代码语言:javascript复制@RequestMapping("/matc*")
public String matchMore(){
return "success";
}
匹配0个或多个字符
也可以匹配一层路径
代码语言:javascript复制@RequestMapping("/matc*/layer")
public String matchOneLayer(){
return "success";
}
”**“ 匹配0或者多层
代码语言:javascript复制@RequestMapping("/match/**/layers")
public String matchMoreLayers(){
return "success";
}
四、@PathVariable注解
通过@PathVariable注解可以将URL中占位符参数绑定到控制器处理方法的入参中
新建一个PathVarController
代码语言:javascript复制@Controller
public class PathVarController {
// 路径上可以有站位符,*也是占位符,但是不能获取位置上变量的值,{}可以获取变量的值
// 方法参数中定义变量接收路径中的变量的值,并用@PathVariable指定路径变量的名称,默认参数中的变量名一致
@RequestMapping("/user/{id}")
public String userInfo(@PathVariable("id") String id){
System.out.println("路径参数id的值为:" id);
return "success";
}
}
控制它打印出路径中变量的值
只能占一层路径,如果想要获取两层路径就要定义两个占位符
代码语言:javascript复制@RequestMapping("/user/{id}/{orderId}")
public String userInfoDetial(@PathVariable("id") String id, @PathVariable("orderId") String orderId){
System.out.println("路径参数id的值为:" id);
System.out.println("路径参数orderId的值为:" orderId);
return "success";
}
REST风格URL
REST:即Representational State Transfer,表现层状态转化,是一种软件架构, REST结构清晰,符合标准,易于理解,扩展方便
- 资源(Resources):网络上的资源,如文本图片信息等,可以用一个URI(统一资源定位符)来指向,每种资源对应一个特定的URI,可以通过访问URI或者资源
- 表现层(Representation):把资源具体呈现出来的形式,即表现层,如JSON格式,XML格式,TXT格式
- 状态转化(State Transfer):每发出一个请求,就代表客户端和服务端的一次交互。HTTP协议是一种无状态的协议,所有的状态都保存在服务器端,客户端想要操作服务器,必须通过某种手段,让服务器端状态发生变化,这种转化建立在表现层之上。HTTP协议里有四种操作方式,PUT表示更新资源,GET表示获取资源,POST表示新增资源,DELETE表示删除资源