SpringMVC——SpringMVC概述、第一个SpringMVC程序、RequestMapping映射、Rest风格介绍

一、MVC介绍

MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责。

  • 模型:用于存储数据以及处理用户请求的业务逻辑。
  • 视图:向控制器提交数据,显示模型中的数据。
  • 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

基于 Servlet 的 MVC 模式的具体实现如下。

  • 模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)。
  • 视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。
  • 控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

在这里插入图片描述

二、SpringMVC简介

  • Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。

官方文档:https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web

SpringMVC工作流程:

Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的,DispatcherServlet 用来处理所有的 HTTP 请求和响应。
在这里插入图片描述
在这里插入图片描述
下面是对应于 DispatcherServlet 传入 HTTP 请求的事件序列:

  • 客户端请求提交到DispatcherServlet
  • 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
  • DispatcherServlet将请求提交到Controller(也称为Handler)
  • Controller调用业务逻辑处理后,返回ModelAndView
  • DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
  • 视图负责将结果显示到客户端

DispatcherServle(前端控制器)

没有前端控制器:
在这里插入图片描述
有了前端控制器:
在这里插入图片描述
我们发现这个前端控制器,很像web里面的Filter过滤器;

从这里能看出使用MVC框架必须在web.xml中配置前端控制器, 一般的要么是要Filter , 要么是Servlet.

  • Struts2基于Filter.
  • SpringMVC基于Servlet

常用主要组件:

  • DispatcherServlet:前端控制器
  • Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理
  • HandlerMapping :请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutiongChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)
  • ViewResolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,很容易更换其他视图技术;
  • 如InternalResourceViewResolver将逻辑视图名映射为JSP视图
  • LocalResolver:本地化、国际化
  • MultipartResolver:文件上传解析器
  • HandlerExceptionResolver:异常处理器

三、第一个SpringMVC——hello world

1、导包
SpringMVC是Spring的web模块,所有模块的运行都是依赖核心模块(IOC模块)

核心容器模块

  commons-logging-1.1.3.jar
  spring-aop-4.0.0.RELEASE.jar
  spring-beans-4.0.0.RELEASE.jar
  spring-context-4.0.0.RELEASE.jar
  spring-core-4.0.0.RELEASE.jar
  spring-expression-4.0.0.RELEASE.jar

web模块(两个基础的web模块)

  spring-web-4.0.0.RELEASE.jar
  spring-webmvc-4.0.0.RELEASE.jar

2、写配置

  • web.xml配置

    想让 DispatcherServlet 处理的请求,通过使用在 web.xml 文件中的一个 URL 映射

    在初始化 DispatcherServlet 时,该框架将尝试加载位于该应用程序的WebContent/WEB-INF 目录中文件名为[servlet-name]-servlet.xml的应用程序内容

    servlet-mapping 标签表明哪些 URLs 将被 DispatcherServlet 处理

    <?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_2_5.xsd" id="WebApp_ID" version="2.5">
      <display-name>01_SpringMVC_helloworld</display-name>
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      
      <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
             <!--配置前端控制器-->
        <!--1.注册DispatcherServlet-->
    	<servlet>
    		<servlet-name>springDispatcherServlet</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<!-- contextConfigLocation:指定SpringMVC配置文件位置 -->
    		<!--关联一个springMVC的配置文件:【servlet-name】-servlet.xml-->
            <!--默认去WEB-INF目录中去寻找-->
            <!-- 默认寻找<servlet-name>元素文本内容-servlet.xml文件-->
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>classpath:springmvc.xml</param-value>
    		</init-param>
    		<!-- servlet启动加载,servlet原本是第一次访问创建对象
    			load-on-startup:服务器启动的时候创建对象,值越小优先级越高,越先创建对象
    		 -->
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    
    	<!-- Map all requests to the DispatcherServlet for handling -->
    	<servlet-mapping>
    		<servlet-name>springDispatcherServlet</servlet-name>
    		<!-- 
    			/*和/都是拦截所有请求
    			/:会拦截所有请求,但不会拦截*.jsp,能保证jsp访问正常
    			/*的范围更大,还会拦截到*.jsp这些请求,一旦拦截jsp页面就不能显示了
    		 -->
    		<url-pattern>/</url-pattern>
    	</servlet-mapping>
    </web-app>
    
  • Spring框架配置

    <context:component-scan>标签进行包扫描

    <?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:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    	
    	<!-- 扫描所有组件 -->
    	<context:component-scan base-package="com.zb"></context:component-scan>
    	
    	<!-- 配置一个视图解析器,能帮我们拼接页面地址 -->
    	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="prefix" value="/WEB-INF/pages/"></property>
    		<property name="suffix" value=".jsp"></property>
    	</bean>
    </beans>
    

3、定义控制器(@Controller注解)

DispatcherServlet 发送请求到控制器中执行特定的功能。@Controller注释表明一个特定类是一个控制器的作用。@RequestMapping注释用于映射 URL 到整个类或一个特定的处理方法。

@RequestMapping(method = RequestMethod.GET) 用于声明 myFirstRequest() 方法作为控制器的默认 service 方法来处理 HTTP GET 请求。

@Controller
public class MyFirstController {
    
    
	
	/**
	 * 表示从当前项目下开始,处理当前项目下的hello请求
	 * 值属性表明 URL 映射到哪个处理方法,方法属性定义了 service 方法来处理 HTTP GET 请求。
	 */
	@RequestMapping(value="/hello", method = RequestMethod.GET)
	public String myFirstRequest(){
    
    
		System.out.println("收到了请求。。。。");

		//视图解析器自动拼串
//		<property name="prefix" value="/WEB-INF/pages/"></property>
//		<property name="suffix" value=".jsp"></property>
		return "success";//类似于请求转发的功能
	}
}

4、测试

在这里插入图片描述

运行流程:

客户端点击链接会发送 http://localhost:8080/springmvc/hello 请求
来到tomcat服务器;
SpringMVC的前端控制器收到所有请求;
来看请求地址和@RequestMapping标注的哪个匹配,来找到到底使用那个类的哪个方法来处理
前端控制器找到了目标处理器类和目标方法,直接利用返回执行目标方法;
方法执行完成以后会有一个返回值;SpringMVC认为这个返回值就是要去的页面地址
拿到方法返回值以后;用视图解析器进行拼串得到完整的页面地址;
拿到页面地址,前端控制器帮我们转发到页面;

四、关于hello world的几个细节

4.1 如果不指定xml配置文件位置?

在初始化DispatcherServlet 时,该框架将尝试加载位于该应用程序的 WebContent/WEB-INF 目录中文件名为[servlet-name]-servlet.xml的应用程序内容。即如果servlet-name元素文本内容是hello,则去WEB-INF目录下寻找hello-servlet.xml文件。

如果不想使用默认文件名,可以通过init-param元素指定xml文件的路径

在这里插入图片描述

4.2 url-pattern元素

配置成/,可以支持流行的RESTful风格,但会导致静态文件(jpg,js,css)被拦截后不能正常响应

配置成/*,是错误的方式,可以请求到Controller中,但跳转到jsp是被拦截,不能渲染jsp视图。
在这里插入图片描述

五、RequestMapping映射

  • RequestMapping是一个处理请求地址映射的注解,可用在类或方法上。

    用在类上,表示类中的所有响应请求的方法都是以改地址作为父路径。
    用在方法上:提供进一步的细分映射信息。相对于标记在类上的 URL。
    若类上未标注 @RequestMapping,则方法处标记的 URL 相对于 WEB 应用的根目录
    标在类上:

    /**
     * 为当前类所有的方法的请求地址指定一个基准路径
     * @author ZB
     */
    // 这个/是可以省略,即使省略了,也是默认从当前项目下开始;习惯加上比较好
    @RequestMapping("/root")
    @Controller
    public class RequestMappingTestController {
          
          
    	//访问地址需要是:root/handle01
    	@RequestMapping("/handle01")
    	public String handle01(){
          
          
    		System.out.println("RequestMappingTestController...handle01");
    		return "success";
    	}
    }
    
  • RequestMapping注解的六个属性:

    1、value,method
    value: 指定请求的url
    method:指定请求的method类型,GET、POST、PUT、DELETE等。

    2、consumes,produces
    consumes:只接受内容类型是哪种的请求,规定请求头中的Content-Type
    produces:告诉浏览器返回的内容类型是什么,给响应头中加上Content-Type:text/html;charset=utf-8

    3、params、heanders
    params:指定request中必须包含某些参数值,包含该方法才处理。
    heanders:指定request中必须包含指定的header,包含才能处理。

  • 作用:

    DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。

5.1 RequestMapping映射请求方式

method:限定请求方式。默认是什么请求都可以,即不管是post还是get请求都可以接受
 		HTTP协议中的所有请求方式:
			【GET】, HEAD, 【POST】, PUT, PATCH, DELETE, OPTIONS, TRACE
		GET、POST
		method=RequestMethod.POST:只接受POST类型的请求;
			不是规定的方式报错:4xx:都是客户端错误
 				405 - Request method 'GET' not supported
	//限定请求方法,只接受POST请求,如果不是post请求访问会报:405 - Request method 'GET' not supported
	@RequestMapping(value="/handle02",method=RequestMethod.POST)
	public String handle02(){
    
    
		System.out.println("我只接受POST请求");
		return "success";
	}

5.2 RequestMapping映射请求参数&请求头

params:规定请求参数
params 和 headers支持简单的表达式:
	param1: 表示请求必须包含名为 param1 的请求参数
		eg:params={
    
    "username"}:
			发送请求的时候必须带上一个名为username的参数;没带都会404
	!param1: 表示请求不能包含名为 param1 的请求参数
		eg:params={
    
    "!username"}
			发送请求的时候必须不携带上一个名为username的参数;带了都会404
	param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
		eg:params={
    
    "username!=123"}
			发送请求的时候;携带的username值必须不是123(不带username或者username不是123)
 
	{
    
    “param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
		eg:params={
    
    "username!=123","pwd","!age"}
			请求参数必须满足以上规则;
			请求的username不能是123,必须有pwd的值,不能有age
			
headers:规定请求头;也和params一样能写简单的表达式

params测试:

//发送请求的时候;携带的username值必须不是123(即不带username或者username不是123),否则会报404
@RequestMapping(value="handle03",params={
    
    "username!=123"})
public String handle03(){
    
    
	return "success";
}

//发送请求的时候必须携带上一个名为username的参数且值为123,并且必须有pwd的值,不能有age,否则会报404
@RequestMapping(value="handle05",params={
    
    "username=123","pwd","!age"})
public String handle05(){
    
    
	return "success";
}

headers测试:

/**
 * User-Agent:浏览器信息;
 * 让火狐能访问,让谷歌不能访问
 * 
 * 谷歌:
 * User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
 * 火狐:
 * User-Agent	Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
 */
//设置只能火狐浏览器访问
@RequestMapping(value="/handle04",headers={
    
    "User-Agent=Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"})
public String handle04(){
    
    
	System.out.println("handle04....");
	return "success";
}

5.3 RequestMapping支持Ant 路径风格

  • ?:能替代任意一个字符
  • *:能替代任意多个字符,和一层路径
  • **:能替代多层路径

代码测试:

/**
 * @RequestMapping模糊匹配功能
 * URL地址可以写模糊的通配符:
 */
@Controller
public class RequestMappingTest {
    
    
	
	@RequestMapping("/antTest01")
	public String antTest01(){
    
    
		System.out.println("antTest01...");
		return "success";
	}
	
	/**
	 * ? 匹配一个字符,0个多个都不行
	 */
	@RequestMapping("/antTest0?")
	public String antTest02(){
    
    
		System.out.println("antTest02...");
		return "success";
	}
	
	/**
	 * *匹配任意多个字符,也可以匹配0个字符
	 */
	@RequestMapping("/antTest0*")
	public String antTest03(){
    
    
		System.out.println("antTest03...");
		return "success";
	}
	
	/**
	 * 	*也可以匹配一层路径
	 */
	@RequestMapping("/a/*/antTest04")
	public String antTest04(){
    
    
		System.out.println("antTest04...");
		return "success";
	}
	
	/**
	 *  **可以匹配多层路径
	 */
	@RequestMapping("/a/**/antTest05")
	public String antTest05(){
    
    
		System.out.println("antTest05...");
		return "success";
	}
}

5.4 RequestMapping映射请求占位符PathVariable注解

  • @PathVariable 映射 URL 绑定的占位符
  • 带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
  • 通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中
    URL 中的{xxx}占位符可以通过@PathVariable("xxx")绑定到操作方法的入参中。

代码示例:

//路径上可以有占位符:  占位符 语法就是可以在任意路径的地方写一个{变量名}
// 路径上的占位符只能占一层路径
@RequestMapping("/user/{id}")	//访问路径为:/user/admin
public String pathVariableTest(@PathVariable("id")String id){
    
    
	System.out.println("路径上的占位符的值"+id);//输出:路径上的占位符的值admin
	return "success";
}

六、Rest风格详解

6.1 Rest概述

REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用

  • 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。
    它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。
    可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。
    获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
  • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
  • 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。
    而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。
  • 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

6.2 REST风格

传统CRUD请求方式

/getBook?id=1   	查询图书
/deleteBook?id=1	删除1号图书
/updateBook?id=1	更新1号图书
/addBook     		添加图书

REST风格请求方式

/book/1          	GET-----查询1号图书
/book/1          	PUT------更新1号图书
/book/1          	DELETE-----删除1号图书
/book               POST-----添加图书

即:简洁的URL提交请求,以请求方式区分对资源的操作

6.3 完成REST风格的CRUD

  1. HiddenHttpMethodFilter的引入

    浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,
    Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。

  2. HiddenHttpMethodFilter中doFilterInternal源码分析

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    		throws ServletException, IOException {
          
          
    	//获取表单上_method带来的值
    	String paramValue = request.getParameter(this.methodParam);
    	//判断如果表单是一个post而且_method有值
    	if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
          
          
    		String method = paramValue.toUpperCase(Locale.ENGLISH);
    		//重写了request.getMethod()方法,返回method
    		HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
    		//wrapper.getMethod()得到的即为 method的值
    		filterChain.doFilter(wrapper, response);
    	}
    	else {
          
          
    		//
    		filterChain.doFilter(request, response);
    	}
    }
    
  3. 完成REST风格的CRUD(重点)

     1、配置HiddenHttpMethodFilter
     	(SpringMVC中有一个Filter;他可以把普通的请求转化为规定形式的请求;配置这个filter)
     2、需要发送POST请求
     3、需要在发送POST请求时携带一个 name="_method"的隐含域,值为PUT或DELETE
    

    ①xml文件配置HiddenHttpMethodFilter

    <!-- 支持REST风格的过滤器:可以将POST请求转换为PUT或DELETE请求 -->
    <filter>
    	<filter-name>HiddenHttpMethodFilter</filter-name>
    	<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
    	<filter-name>HiddenHttpMethodFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>
    

    ②控制器方法:

    @Controller
    public class BookController {
          
          
    	
    	//查询图书操作
    	@RequestMapping(value="/book/{id}",method=RequestMethod.GET)
    	public String getBook(@PathVariable("id")String id){
          
          
    		System.out.println("查询编号为"+id+"的图书");
    		return "success";
    	}
    	//增加图书操作
    	@RequestMapping(value="/book",method=RequestMethod.POST)
    	public String addBook(){
          
          
    		System.out.println("增加标号为图书成功");
    		return "success";
    	}
    	
    	//删除图书操作
    	@RequestMapping(value="/book/{id}",method=RequestMethod.DELETE)
    	public String deleteBook(@PathVariable("id")String id){
          
          
    		System.out.println("删除编号为"+id+"的图书");
    		return "success";
    	}
    	//修改图书操作
    	@RequestMapping(value="/book/{id}",method=RequestMethod.PUT)
    	public String updateBook(@PathVariable("id")String id){
          
          
    		System.out.println("更新编号为"+id+"的图书");
    		return "success";
    	}
    	
    }
    

    ③请求代码:

    <!-- 测试REST风格GET请求 -->
    <a href="book/1">查询图书</a><br/>
    <!-- 测试REST风格POST请求 -->
    <form action="book" method="post">
    	<input type="submit" value="增加图书信息"/>
    </form>
    <!-- 测试REST风格DELETE请求 -->
    <form action="book/1" method="post">
    <!-- delete不区分大小写 -->
    	<input type="hidden" name="_method" value="DELETE"/>
    	<input type="submit" value="删除图书信息"/>
    </form>
    <!-- 测试REST风格PUT请求 -->
    <form action="book/1" method="post">
    	<input type="hidden" name="_method" value="PUT"/>
    	<input type="submit" value="修改图书信息"/>
    </form>
    
  4. 注意:高版本Tomcat;Rest支持有点问题(不接受DELETE、PUT请求)
    在这里插入图片描述
    解决方案:
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44630656/article/details/115071642