一、SpringMVC入门
服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序. 使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构
三层架构:
表现层:WEB层,用来和客户端进行数据交互的。表现层一般会采用MVC的设计模型
业务层:处理公司具体的业务逻辑的
持久层:用来操作数据库的
MVC全名是Model View Controller 模型视图控制器,每个部分各司其职。
Model:数据模型,JavaBean的类,用来进行数据封装。
View:指JSP、HTML用来展示数据给用户
Controller:用来接收用户的请求,整个流程的控制器。用来进行数据校验等(Hibernate Validator)
SpringMVC
SpringMVC是一种基于Java的、实现MVC设计模型的、请求驱动类型的(基于HTTP协议)、轻量级Web框架,属于 Spring FrameWork 的后续产品。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
SpringMVC已经成为目前最主流的 MVC 框架之一,并且随着Spring3.0的发布,全面超越 Struts2,成为最优秀的 MVC(web层的) 框架。
它通过一套注解,让一个简单的Java类成为处理请求的控制器(Controller),而无须实现任何接口(跟Servlet对比)。同时它还支持RESTful编程风格的请求。
SpringMVC的优点
-
清晰的角色划分:
前端控制器(DispatcherServlet)
请求到处理器映射(HandlerMapping) :负责找controller类
处理器适配器(HandlerAdapter) : 负责调用controller类,得到结果,封装结果
视图解析器(ViewResolver) : 负责解析view
处理器或页面控制器(Controller)
验证器( Validator)
命令对象(Command 请求参数绑定到的对象就叫命令对象)
表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。
-
分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要。
-
由于命令对象就是一个 POJO, 无需继承框架特定 API,可以使用命令对象直接作为业务对象。
-
和 Spring 其他框架无缝集成,是其它 Web 框架所不具备的。
-
可适配,通过 HandlerAdapter 可以支持任意的类作为处理器。
-
可定制性, HandlerMapping、 ViewResolver 等能够非常简单的定制。
-
功能强大的数据验证、格式化、绑定机制。
-
利用 Spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试。
-
本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
-
强大的 JSP 标签库,使 JSP 编写更容易。
-
有比如RESTful风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配
置支持等等。
小结
-
Spring MVC 是Spring开发的关于Web层的框架
-
它的作用:接收请求,调用service,响应结果
代码实现
导入依赖
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--jsp-api-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
编写页面
在webapp里创建
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是首页~</h2>
<a href="/sayHi">点我发起请求</a>
</body>
</html>
在webapp里创建
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是成功的页面~</h2>
</body>
</html>
编写Controller
在com.execise.controller
包中创建类Controller01
类上增加@Controller
注解,声明成为一个bean
创建sayHi方法
,并在方法上增加@RequestMapping
注解,声明方法的访问路径
package com.execise.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*
入门案例
1. 定义类,类上打注解 @Controller
2. 定义方法, 方法上打注解 @RequestMapping
3. 这个controller能处理 /sayHi的请求,但是要想处理请求,首先必须能抓住请求。
4. 所以要在web.xml配置servlet :: DispatcherServlet
*/
@Controller
public class Controller01 {
@RequestMapping("/sayHi")
public String sayHi(){
System.out.println("调用了Controller01的sayHi方法~!~");
return "/success.jsp";
}
}
编写配置文件
在
resources
中创建springmvc的配置文件springmvc.xml
, 这个名字可以随意,也可以写成前几天的applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--1. 扫描包-->
<context:component-scan base-package="com.execise"/>
</beans>
修改Web.xml
在
webapp/WEB-INF/web.xml
中配置前端控制器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"
version="2.5">
<!--登记|注册 DispatcherServlet,让它来抓请求-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--1. 告诉DispatcherServlet,springmvc配置文件在哪里。它会解析这个文件,进而去扫描包-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--2. 让DispatcherServlet初始的时机提前到服务器启动时-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispacher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
运行测试
启动项目,点击首页的超链接访问Controller
sayHi
方法被访问到,并且页面跳转到了success.jsp
小结
-
创建web工程,导入依赖:
spring-webmvc, servlet-api, jsp-api
-
编写Controller
-
提供springmvc.xml:开启组件扫描
-
修改web.xml:配置前端控制器DispatcherServlet
配置详解
求响应流程
三大组件
HandlerMapping
处理器映射器
- 作用:根据客户端请求的资源路径,查找匹配的
Controller
及拦截器(类似过滤器)链
HandlerAdapter
处理器适配器
- 作用:用于适配调用不同的
Controller
执行Controller,得到模型和视图
ViewResolver
视图解析器
- 作用:用于解析视图,根据视图路径找到真实视图(页面)
详细执行流程
-
客户端发请求到
DispatcherServlet
-
DispatcherServlet
-
通过
HandlerMapping
处理器映射器,根据请求路径,查找匹配的Controller
及拦截器 -
得到要执行的
Controller
和拦截器(执行链)
-
-
DispatcherServlet
-
通过
HandlerAdapter
处理器适配器,调用控制器Controller
-
得到
ModelAndView
对象(其中View指视图路径,Model要响应的数据对象)
-
-
DispatcherServlet
-
通过
ViewResolver
解析视图,得到真实视图(视图路径对应的页面) -
渲染视图(把Model里的数据填充到View里 , 替换页面上的 EL表达式为真实的数据)
-
-
把最终渲染的结果,响应到客户端
springmvc.xml的配置
跟之前的 applicationContext.xml
是一样的。只是换了个名字而已。springmvc和spring可以共用同一个配置文件!
基本配置示例
<!--开启组件扫描-->
<context:component-scan base-package="com.execise.controller"/>
<!--开启mvc的注解驱动-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 静态资源的配置:让SpringMVC不处理静态资源 -->
<mvc:default-servlet-Handler/>
mvc的注解驱动
<!--
2. 注解驱动
2.1 声明时一定要选择mvc的标签,如果选择到cache的标签,则会出现这个错误:
No bean named 'cacheManager' available
2.2 这个标签会帮助我们在项目里面导入三大组件
2.3 后面用到的一些注解,必须要有这个标签的支撑,否则注解用不了! eg:@ResponseBody
-->
<mvc:annotation-driven/>
配置说明
在SpringMVC中,处理器映射器,处理器适配器,视图解析器 被称为三大组件
在springmvc.xml中配置<mvc:annotation-driven/>
标签,可以加载SpringMVC的组件
-
如果没有此项配置,SpringMVC也会自动加载组件,所以现在的使用中不配置这一项也可以
-
但是在之后,这个标签还有其它作用,所以要配置上
视图解析器设置
视图路径的配置方式
SpringMVC把页面称为视图,例如JSP|HTML页面就是视图。在Controller
的方法中,返回的字符串就是跳转的视图(页面)路径
视图的路径有两种写法:
-
物理视图:
/success.jsp | success.jsp
,即:视图的真实路径(完整路径)-
直观,但是写起来麻烦
-
例如: 如果有一个页面page.jsp 位于 /webapp/a/b/c/page.jsp , 那么物理视图返回: “/a/b/c/page.jsp”;
-
-
逻辑视图:
success
,需要配合视图解析器,才能得到真实路径- 不直观,但是写起来简单
- 例如: 如果有一个页面page.jsp 位于 /webapp/a/b/c/page.jsp , 那么逻辑视图返回: “page”
物理视图的配置方式
-
在
Controller
的方法中,直接返回物理视图路径。 -
不需要配置视图解析器
@Controller
public class Controller01 {
//物理视图跳转: 要写完整的路径地址
@RequestMapping("/sayHi02")
public String sayHi02(){
System.out.println("调用了Controller01的sayHi02方法~!~");
return "/a/b/c/success.jsp";
}
}
逻辑视图的配置方式
在springmvc.xml
中增加以下内容:
<!--
3. 视图解析器
3.1 视图解析器是搭配逻辑视图使用的,它要和方法的返回值拼接起来得到页面的真实路径
前缀 + 方法的返回值 + 后缀 ==== /success.jsp
3.2 视图解析器一旦配置,就会对全局产生影响,所有的controller的方法返回值都要受到它的影响
除了某些方法之外【后面讲!】
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
在Controller
中修改代码,简化方法返回值
@Controller
public class Controller01 {
//逻辑视图跳转:只要写页面的名字即可,需要搭配视图解析器来使用
@RequestMapping("/sayHi03")
public String sayHi03(){
System.out.println("调用了Controller01的sayHi03方法~!~");
return "success";
}
}
由视图解析器,帮我们把prefix + "success" + suffix
,拼接成物理视图/success.jsp
处理静态资源
请求静态资源的说明
-
使用SpringMVC时,客户端访问静态资源时,会访问不到 , 会得到404
-
Tomcat本身具备处理静态资源的能力,但是我们配置的
DispatcherServlet
把Tomcat的默认处理器覆盖掉了;而DispatcherServlet
没有处理静态资源的能力,所以:访问不到静态资源Tomcat里面有一个Servlet,可以处理静态资源: DefaultServlet,它的映射路径是 /
我们使用SpringMVC的时候,在web.xml中,配置DispatcherServlet,配置的地址路径也是 /
两种配置方式解决问题
指定静态资源的位置
针对客户端对静态资源的请求,指定资源所在的位置。让SpringMVC直接去指定目录下加载
示例:
<!-- 4. 静态资源处理:
4.1 当我们的DispatcherServlet配置的映射路径是 / 的时候,静态资源就看不到了,报:404的错误
a. 在tomcat里面有一个默认的DefaultServlet, 它的映射路径是 / 它的作用是专门用来处理静态资源
b. 由于DispatcherServlet,映射路径也是 / 所以咱们的DispatcherServlet把DefaultServlet给覆盖了。而DispatcherServlet 又不能处理静态资源,所以出现404
4.2 解决静态资源的问题,有两种办法:
1.不要将DispatcherServlet的映射路径配置为/ 配置为*.do就可以了
【不想替换 因为每次发起请求都要带一个.do的尾巴】
2.DispatcherServlet映射路径依然配置为/,如何解决
a. 配置映射地址和资源路径。
b. 把静态资源的请求,交还给tomcat的DefaultServlet处理
-->
<!--
1. 配置地址和路径的映射
a. 不推荐使用这种办法,因为以后如果还有新的静态资源的类型,那么就需要在这里再增加一行配置
b. 解释:
mvc:resources : 用来做请求地址和资源路径映射
mapping : 请求地址
* : 表示当前的位置,只能匹配一级目录
/html/* ========不能匹配==========localhost:82/html/my.html
/html/* ========不能匹配==========localhost:82/html/a/b/c/my.html
** : 表示匹配任意目录
/html/** ========能匹配==========localhost:82/html/my.html
/html/** ========能匹配==========localhost:82/html/a/b/c/my.html
location : 静态资源所在位置 , 后面要有 / 结尾
-->
<mvc:resources mapping="/html/**" location="/html/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
由Tomcat处理静态资源(推荐)
如果客户端请求了静态资源,DispatcherServlet处理不了,就交给Tomcat的原生Servlet来处理
示例:
<!--2. 把静态资源的处理,交给tomcat的DefaultServlet来做-->
<mvc:default-servlet-handler/>
小结
配置视图解析器
-
Controller里的方法返回值要写逻辑视图
-
再配置视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
如果有静态资源要访问,把静态资源仍然交给Tomcat处理
<mvc:default-servlet-handler/>
开启mvc的注解驱动:会注册一些组件,提供一些功能
<mvc:annotation-driven/>
开启组件扫描
<context:component-scan base-pcakge="com.execise.controller"/>
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_2_5.xsd"
version="2.5">
<!--登记|注册 DispatcherServlet,让它来抓请求-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--1. 告诉DispatcherServlet,springmvc配置文件在哪里。它会解析这个文件,进而去扫描包-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--2. 让DispatcherServlet初始的时机提前到项目发布-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
DispatcherServlet的映射路径应该写什么呢?
1. 映射路径能不能写成 *.do *.execise这样的写法呢?【也就是说,只抓尾巴】
a. 可以!并且它不会覆盖掉Tomcat里面的DefaultServlet,也不需要写静态资源处理配置
b. 有一点不好的地方就是:访问所有的controller方法,都必须带上尾巴 .do
2. 映射路径能不能写成 /* 这样的写法呢?
不可以! jsp页面看不到了! 看到的只是jsp页面的源码
a. 在tomcat里面还有一个Servlet叫做:JspServlet, 它专门用来处理jsp页面的请求
b. 这个JspServlet的映射路径是 *.jsp
c. 现在DispatcherServlet,映射的路径是 /* ,那么就会涉及到一个优先级的问题!
d. 映射路径的优先级是这样: /aa > /* > *.jsp | *.do > /
综上所述:
1. 当DispatcherServlet配置的映射路径是 /*的时候,浏览器访问jsp页面这个请求,也会被它抓住
但是DispatcherServlet又不能解析,执行jsp页面,所以直接把这个页面的源码丢给了浏览器!
2. /表示缺省配置,优先级是最低!所谓的缺省配置就是当没有任何servlet处理这个请求的时候,就由它来处理!
-->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置说明
load-on-startup
:配置Servlet的创建时机,值是整数
-
如果是正整数,表示服务器一启动就要创建Servlet对象。数值越小,创建的越早
-
如果是0或者负数,表示默认的:第一次访问时,创建Servlet对象
DispatcherServlet
是SpringMVC一切功能的基础和核心,要求:服务器启动时就创建,并且要最早创建,所以设置值为1
init-param
:配置Servlet的初始化参数
contextConfigLocation
:配置springmvc.xml的路径,让DispatcherServlet被创建时,加载配置文件,初始化Spring容器
url-pattern
:配置Servlet的路径,通常配置为/
拓展:
DispatcherServlet
配置成/
和/*
的区别:
- 对JSP的处理不同。当客户端请求了
xxx.jsp
时
如果DispatcherServlet
配置的是/*
,不能正常访问JSP
-
/*
是目录匹配,优先级高于扩展名匹配(Tomcat里有JspServlet
,路径是*.jsp
) -
必定是由
DispatcherServlet
来处理JSP,但是DispatcherServlet不具备查找和处理jsp的能力,会报错
如果DispatcherServlet
配置的是/
,可以正常访问JSP
-
/
是缺省匹配,优先级低于扩展名匹配(Tomcat里有JspServlet
,路径是*.jsp
) -
必定是由Tomcat来处理JSP,Tomcat本身具备查找和处理JSP的能力,可以正常访问JSP
完全路径匹配 > 目录匹配 > 扩展名匹配 > 缺省匹配
/aa > /* > *.do > /
controller的配置
package com.execise.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.Servlet;
/*
@Controller :
1. 表示这个类是一个控制器,可以处理请求。
2. spring会把这个类管理起来, 创建这个类的对象。
RequestMapping :
1. 表示方法的映射地址,当什么请求来的时候,会执行这个方法。
2. 属性:
value|path : 用来设置映射路径
method : 用来设置请求方式,表示什么样的请求方式,才能执行这个方法。【默认是都支持!】
RequestMethod.GET : 允许get请求到这里来
RequestMethod.POST : 允许post请求到这里来
如果设置了post,页面还是用get请求过来,那么会报错:
Request method 'GET' not supported
params : 用来要求请求一定要携带指定名称的参数
params="username"`:必须提交了 名称为username的表单参数,才可以访问
params="username=tom"`:必须提交了 名称为username、值为tom的表单参数,才可以访问
params="username!=tom"`:提交了表单参数 名称为username、值不是tom, 才可以访问
3. 这个注解可以打在方法身上,也可以打在类身上!
3.1 在类身上写@RequestMapping,访问方法时,需要加上类上面的映射路径
3.2 这样子做的好处主要是为了和其他的模块进行区分。有前缀的划分。
*/
@Controller
@RequestMapping("/user")
public class Controller01 {
@RequestMapping(value = "/sayHi04", method = RequestMethod.GET , params = "username")
public String sayHi04(){
System.out.println("调用了Controller01的sayHi04方法~!~");
return "success";
}
}
配置说明
@RequestMapping
注解 , 通常用在Controller
里,用于设置访问路径
注解语法
@RequestMapping(
value="访问路径",
method=请求方式,
params="请求参数"
)
常用属性:
-
value/path:访问路径,即:什么路径能够访问到这个方法
-
method:请求方式,即:什么请求方式能够访问这个方法。从枚举
RequestMethod
中取值-
RequestMethod.POST
:必须是POST方式,才可以访问到 -
RequestMethod.GET
:必须是GET方式,才可以访问到
-
-
params:请求参数,即:请求携带了什么样的参数能够访问这个方法(了解)
-
params="username"
:必须提交了 名称为username的表单参数,才可以访问 -
params="username=tom"
:必须提交了 名称为username、值为tom的表单参数,才可以访问 -
params="username!=tom"
:提交了表单参数 名称为username、值不是tom, 才可以访问
-
-
如果注解用在Controller类上
-
表示设置访问路径的一级目录,要和方法上的路径组装成访问路径。用于模块化管理,例如:
-
类上有注解
@RequestMapping("/user")
-
类里方法上有注解
@RequestMapping("/save")
-
那么方法的访问路径是:
/user/save
-
-
二、获取请求数据
请求参数的绑定
绑定机制
表单提交的数据都是key=value格式的(username=zs&password=123), SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的(要求:提交表单的name和方法的参数的名称是相同的)
<form action="">
用户名:<input type="text" name="username"/>
密码: <input type= "password" name="password"/>
<input type="submit"/>
</form>
public String register(String username , String password){
}
//======================================================
class User{
private String username;
private String password;
}
public String register02(User user){
}
支持的数据类型
-
基本数据类型和字符串类型
-
实体类型(JavaBean)
-
集合数据类型(List、map集合等)
使用要求
-
如果是基本类型或者 String 类型: 要求我们的参数名称必须和controller中方法的形参名称保持一致。 (严格区分大小写) .
-
如果是 对象 类型,或者它的关联对象: 要求表单中参数名称和对象类的属性名称保持一致
-
如果是集合类型,有两种方式:
-
第一种:要求集合类型的请求参数必须在对象类 中。在表单中请求参数名称要和 对象类中集合属性名称相同。给 List 集合中的元素赋值, 使用下标【】。给 Map 集合中的元素赋值, 使用键值对。
-
第二种:接收的请求参数是 json 格式数据。需要借助一个注解实现 @RequestBody
-
小结
- 绑定机制
SpringMVC的参数绑定过程是把表单提交的请求参数,作为controller里面方法的参数进行绑定的。
- 支持的数据类型 :
基本数据类型和字符串类型
实体类型(JavaBean)
集合数据类型(List、map集合等)
- 使用要求: 名字需要一样
获取请求参数
基本类型和String
说明
-
客户端提交的表单参数名称, 和Controller里方法参数名称相同
-
SpringMVC会自动绑定同名参数,并自动转换类型
页面
index.jsp
<h2>提交简单的参数:</h2>
<form action="/requestSimpleParam" method="post">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="text" name="password"/><br/>
年 龄:<input type="text" name="age"/><br/>
<input type="submit"/>
</form>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是成功的页面~!~</h2>
</body>
</html>
controller
package com.execise.controller;
import com.execise.bean.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@Controller
public class Controller01 {
/*
获取简单的参数:
要求:
1. 方法的参数名字必须和页面的form表单中的name属性的值 一样!
用户名:<input type="text" name="username"/>
*/
@RequestMapping("/requestSimpleParam")
public String requestSimpleParam(String username, String password , int age){
System.out.println("username = " + username);
System.out.println("password = " + password);
System.out.println("age = " + age);
return "success";
}
}
对象类型
SpringMVC会帮我们自动把表单参数,封装成对象,但是要求:
客户端提交的表单参数名称,必须和JavaBean的属性名一样!
Pojo(User)
package com.execise.bean;
import lombok.Data;
@Data
public class User {
private String username;
private String password;
private int age;
}
前端页面
<h2>提交对象的参数:</h2>
<form action="/requestObjectParam">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="text" name="password"/><br/>
年 龄:<input type="text" name="age"/><br/>
<input type="submit"/>
</form>
controller
package com.execise.controller;
import com.execise.bean.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@Controller
public class Controller01 {
/*
获取对象参数:
要求:
1. 页面提交上来的form表单的name属性值必须和JavaBean的属性名字一样!
用户名:<input type="text" name="username"/>
public class User {
private String username;
}
*/
@RequestMapping("/requestObjectParam")
public String requestObjectParam(User user){
System.out.println("user = " + user);
return "success";
}
}
嵌套POJO类型参数
POJO对象中包含POJO对象
public class User {
private String name;
private int age;
private Address address;
//同学们自己添加getter/setter/toString()方法
}
public class Address {
private String province;
private String city;
}
嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
return "{'module':'pojo contain pojo param'}";
}
注意事项:请求参数key的名称要和POJO中属性的名称一致,否则无法封装。
数组
注:只能2接前端多个参数(一个名字对应多个值),不能用List集合。确实需要List集合接收时,使用包装user.list
页面
<h2>提交数组的参数:</h2>
<form action="/requestArrayParam">
请选择您的爱好:<br/>
<input type="checkbox" name="hobby" value="smoke"/>抽烟
<input type="checkbox" name="hobby" value="drink"/>喝酒
<input type="checkbox" name="hobby" value="firehead"/>烫头<br/>
<input type="submit"/>
</form>
Controller01
package com.execise.controller;
import com.execise.bean.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@Controller
public class Controller01 {
/*
获取数组参数:
要求:
1. 页面上的表单项,name属性值必须一样!
2. 方法参数的名字必须和name属性的值一样!
*/
@RequestMapping("/requestArrayParam")
public String requestArrayParam(String [] hobby){
System.out.println("hobby=" + Arrays.toString(hobby));
return "success";
}
}
集合类型参数
集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'module':'list param'}";
}
日期类型参数传递
代码演示
-
日期类型数据基于系统不同格式也不尽相同
2088-08-18
2088/08/18
08/18/2088
-
接收形参时,根据不同的日期格式设置不同的接收方式
//日期参数 http://localhost:80/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/28 8:08:08
//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
System.out.println("参数传递 date ==> "+date);
System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
return "{'module':'data param'}";
}
@DateTimeFormat注解介绍
-
名称:@DateTimeFormat
-
类型:形参注解
-
位置:SpringMVC控制器方法形参前面
-
作用:设定日期时间型数据格式
-
属性:pattern:指定日期时间格式字符串
json数据参数传递
问题:@EnableWebMvc注解和@ResponseBody注解有什么用?
json数据参数介绍
-
json普通数组([“”,“”,“”,…])
-
json对象({key:value,key:value,…})
-
json对象数组([{key:value,…},{key:value,…}])
传递json普通数组
代码演示
- 添加json数据转换相关坐标
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
- 设置发送json数据(请求body中添加json数据)
- 在Controller中编写方法接收json参数
//集合参数:json格式
//1.开启json数据格式的自动转换,在springmvc.xml配置文件中配置开启注解驱动
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list common(json)参数传递 list ==> "+likes);
return "{'module':'list common for json param'}";
}
@RequestBody注解介绍
-
名称:@RequestBody
-
类型:形参注解
-
位置:SpringMVC控制器方法形参定义前面
-
作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
范例:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list common(json)参数传递 list ==> "+likes);
return "{'module':'list common for json param'}";
}
传递json对象
POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数
//POJO参数:json格式
//1.开启json数据格式的自动转换,在springmvc.xml配置文件中配置开启注解驱动
//2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==> "+user);
return "{'module':'pojo for json param'}";
}
传递json对象数组
POJO集合参数:json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数
//集合参数:json格式
//1.开启json数据格式的自动转换,在springmvc.xml配置文件中配置开启注解驱动
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
System.out.println("list pojo(json)参数传递 list ==> "+list);
return "{'module':'list pojo for json param'}";
}
@RequestBody与@RequestParam区别
@RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
@RequestBody用于接收json数据【application/json】
应用
后期开发中,发送json格式数据为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
小结
请求参数类型是简单(基本,String)类型
- 方法的形参和请求参数的name一致就可以
请求参数类型是pojo对象类型
-
形参就写pojo对象
-
pojo的属性必须和请求参数的name一致就可以
提交数组,只能用数组接收,多个参数的name属性名一致,参数要与controller中的参数名一致。
请求参数时json数据类型
-
添加jackson-databind依赖
-
springmvc.xml中开启注解驱动
-
在参数前面加上@RequestBody注解 并且注意对象的属性要和json数据的key一致
细节处理和特殊情况
请求参数乱码
如果请求参数或者响应中有中文就会乱码。在web阶段,我们通过一个自定义的过滤器实现了统一乱码解决
req.setCharacterEncoding(“utf-8”)
resp.setContentType(“text/html;charset=utf-8”);
现在SpringMVC本身 ,也给我们提供了一个过滤器
CharacterEncodingFilter
,用于解决乱码问题 。 只有在post请求才会有中文乱码,如果tomcat > 8.5的版本,那么tomcat已经帮助修复了get请求的中文乱码。如果使用了tomcat7的插件来跑项目, get请求还是会有乱码的!需要配置一下下!
在web.xml里面配置编码过滤器
<!--配置字符编码过滤器-->
<filter>
<filter-name>char</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--指定使用UTF-8的编码 , encoding是固定写法-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>char</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如果使用了tomcat7的插件,get请求会出现中文乱码,那么可以这么解决
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>82</port>
<!-- 请求路径 -->
<path>/</path>
<!--配置编码-->
<uriEncoding>utf-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
自定义类型转换器
默认情况下,SpringMVC已经实现一些数据类型自动转换。 内置转换器全都在:
org.springframework.core.convert.support
包下 ,如遇特殊类型转换要求,需要我们自己编写自定义类型转换器。
场景
页面
<h2>提交包含日期的参数:</h2>
<form action="/requestDateParam">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="text" name="password"/><br/>
出生日期:<input type="text" name="birthday"/><br/>
<input type="submit"/>
</form>
User04
package com.execise.bean;
import lombok.Data;
import java.util.Date;
@Data
public class User04 {
private String username;
private String password;
private Date birthday;
}
Controller01.java
package com.execise.controller;
import com.execise.bean.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@Controller
public class Controller01 {
/*
获取日期格式的数据
*/
@RequestMapping("/requestDateParam")
public String requestDateParam(User04 user04){
System.out.println("user04 = " + user04);
return "success";
}
}
报错了:
自定义类型转换器
步骤:
-
创建一个类实现Converter 接口
-
配置类型转换器
实现:
-
定义一个类,实现 Converter 接口
该接口有两个泛型,S:表示接受的类型, T:表示目标类型(需要转的类型)
package com.execise.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
自定义日期格式转换器
1. 定义类,实现接口Converter
里面有两个泛型: S : 表示的是源数据的类型 , T: 表示的是目标数据的类型
2. 实现方法convert
*/
public class DateConverter implements Converter<String , Date> {
/**
*
* @param source 从页面接收到的数据
* @return 转化出来的日期对象。
*/
public Date convert(String source) {
try {
System.out.println("来到我们的日期转化器了:" + source);
//1. 定义SimpleDateFormat对象
SimpleDateFormat sf =null ;
if(source .contains("-")){
sf = new SimpleDateFormat("yyyy-MM-dd");
}else if(source.contains("/")){
sf = new SimpleDateFormat("yyyy/MM/dd");
}
//2. 转化
return sf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
在springmvc.xml里面配置转换器
spring 配置类型转换器的机制是,将自定义的转换器注册到类型转换服务中去
<!--2. 注解驱动-->
<mvc:annotation-driven conversion-service="cs"/>
<!--
5. 配置日期格式转换器
5.1 配置转换工厂对象
5.2 给它里面的集合 converters 注入我们自己的类型转换器!
-->
<bean id="cs" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<bean class="com.execise.converter.DateConverter"/>
</property>
</bean>
三、响应数据和视图
返回页面视图
返回页面文件名
controller方法返回的字符串会被解析成页面视图(即:页面的地址路径)
返回逻辑视图名称
-
方法返回的字符串,和视图解析器里的前缀、后缀拼接得到真实路径,再进行跳转
-
不管是物理视图(完整的写法)还是逻辑视图(简写),默认采用的都是请求转发跳转
返回带前缀的物理视图
-
请求转发:
forward:/success.jsp
-
重定向:
redirect:/success.jsp
-
注意:如果带有
forward
或者redirect
,那么路径必须是完整的真实路径 , 不受视图解析器影响*
使用示例
package com.execise.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*
响应视图: 跳转页面
1. springmvc跳转页面的时候,默认是采用请求转发去跳转
2. 跳转的写法:
2.1 逻辑视图
只要写上页面的名字即可
2.2 物理视图
要写完整的页面的路径地址才行。
a. 不带前缀的写法
会受到视图解析器的影响
b. 带前缀的写法
不会受到视图解析器的影响
forward:物理视图路径 表示使用请求转发跳转
redirect:物理视图路径 表示使用重定向跳转
*/
@Controller
public class Controller02 {
//逻辑视图跳转: 只要写上视图的名字即可
@RequestMapping("/page01")
public String page01(){
System.out.println("page01...");
return "success";
}
//物理视图跳转: 写上视图的完整路径
@RequestMapping("/page02")
public String page02(){
System.out.println("page02...");
return "/success.jsp";
}
//物理视图跳转:加上前缀 forward:
// forward: 表示 使用请求转发跳转,并且它不会受到视图解析器的影响
@RequestMapping("/page03")
public String page03(){
System.out.println("page03...");
return "forward:/success.jsp";
}
//物理视图跳转,加上前缀 redirect:
// redirect: 表示使用重定向跳转,并且不会受到视图解析器的影响
@RequestMapping("/page04")
public String page04(){
System.out.println("page04...");
return "redirect:/success.jsp";
}
}
请求转发并传递数据
ModelAndView
是SpringMVC提供的组件之一,其中
-
Model
,模型,用于封装数据(Springmvc会把数据放到了request域中) -
View
,视图,就是页面,用于展示数据
如果我们设置了视图名称,并且封装了数据模型,SpringMVC会:
-
把Model的数据放到request域对象中,然后请求转发到指定的视图(页面)
-
我们可以视图页面中获取数据显示出来
使用示例
package com.execise.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/*
跳转页面,并且携带数据到页面上显示!
1. 使用ModelAndView来封装数据和视图
*/
@Controller
public class Controller03 {
//1. 自己new ModelAndView对象
@RequestMapping("/page05")
public ModelAndView page05(){
//1. 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
//2. 装数据: 会把这份数据保存到request作用域里面
mv.addObject("username", "page05");
//3. 装视图 : 会受视图解析器的影响。
mv.setViewName("success");
//4. 返回mv;
return mv ;
}
//在参数里面定义ModelAndView,要求springmvc传递进来对象
@RequestMapping("/page06")
public ModelAndView page06(ModelAndView mv){
//2. 装数据: 会把这份数据保存到request作用域里面
mv.addObject("username", "page06");
//3. 装视图 : 会受视图解析器的影响。
mv.setViewName("success");
//4. 返回mv;
return mv ;
}
//参数传递进来Model对象,封装数据,使用返回值返回视图。
@RequestMapping("/page07")
public String page07(Model model){
//装数据
model.addAttribute("username","page07");
//4. 返回mv;
return "success" ;
}
//========================可以使用以前的request对象或者session对象来存值===============
@RequestMapping("/page08")
public String page08(HttpServletRequest req){
//装数据
req.setAttribute("username", "page08");
//4. 返回mv;
return "success" ;
}
@RequestMapping("/page09")
public String page09(HttpSession session){
//装数据
session.setAttribute("username", "page09");
//4. 返回mv;
return "success" ;
}
//是用重定向的方式跳转页面
@RequestMapping("/page10")
public String page10(HttpSession session){
//装数据
session.setAttribute("username", "page10");
//4. 返回mv;
//return "success" ; //请求转发方式跳转
return "redirect:/success.jsp" ; //重定向方式跳转!
}
}
在index.jsp视图页面中,取出数据显示出来
<h2>这是成功的页面 ${username}</h2>
小结
返回页面文件名
-
返回逻辑视图
-
方法返回的字符串,和视图解析器里的前缀、后缀拼接得到真实路径,再进行跳转
-
是请求转发跳转
-
-
返回带前缀的物理视图 (不受视图解析器的影响)
-
请求转发:
forward:/success.jsp
-
重定向:
redirect:/success.jsp
-
方法返回ModelAndView
public ModelAndView jump(){
ModelAndView mav = new ModelAndView();
mav.setViewName("视图名称");
mav.addObject("数据名称", "值");
return mav;
}
public ModelAndView jump(ModelAndView mav){
mav.setViewName("视图名称");
mav.addObject("数据名称", "值");
return mav;
}
- 方法返回String:视图名称
public String jump(Model model){
model.addAttribute("数据名称", "值");
return "视图名称";
}
返回数据
直接响应字符串
两种方式
-
使用Servlet原生的
response
对象,返回响应数据 response.getWrite().write(“xxxx”); -
方法上使用
@ResponseBody
注解,springmvc就会把方法的返回值当成字符串来看待,不会再识别成页面的地址路径
使用示例
package com.execise.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
返回数据给页面: 字符串 & JSON
*/
@Controller
public class Controller04 {
/*
返回字符串: 使用response对象, 把字符串写出去!
*/
@RequestMapping("/returnStr01")
public void returnStr01(HttpServletResponse resp) throws IOException {
resp.getWriter().write("returnStr01...");
}
/*
返回字符串:
1. 直接在方法的返回值返回字符串
2. 要加上注解 @ResponseBody, springMVC会把返回值看成是字符串,否则会被看成是页面的路径
*/
@ResponseBody
@RequestMapping("/returnStr02")
public String returnStr02(){
return "returnStr02...";
}
}
拓展
如果使用@ResponseBody
响应的中文字符串,即使配置了CharacterEncodingFilter
,也会有乱码
package com.execise.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
返回数据给页面: 字符串 & JSON
*/
@Controller
public class Controller04 {
//=================返回字符串(包含中文)=========================
/*
使用@ResponseBody 返回字符串的时候,如果返回中文字符串,即使设置了编码过滤器,那么页面看到的仍然是乱码!
解决:
有两种办法解决:
1. 使用标准的写法来设置编码,在springmvc.xml中设置
只要配置了,那么对全局都是有效的!
2. 使用投机取巧的办法!
在requestMapping里面使用produces来设置编码,仅对当前的方法生效!
*/
@ResponseBody
@RequestMapping("/returnStr03")
public String returnStr03(){
return "中文03...";
}
// 在requestMapping里面使用produces来设置编码,仅对当前的方法生效!
@ResponseBody
@RequestMapping(value = "/returnStr04", produces = "text/html;charset=utf-8")
public String returnStr04(){
return "中文04...";
}
}
解决方法:在springmvc.xml
里配置如下:
配置SpringMVC的StringHttpMessageConverter
进行字符串处理转换,设置采用utf-8
字符集
<mvc:annotation-driven>
<!--设置消息转换器-->
<mvc:message-converters>
<!--设置从后端给前端返回的字符串的消息转换器,设置它的转换的编码-->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="utf-8"/>
<!--针对不同的数据类型,设置不同的编码-->
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=utf-8</value>
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
返回json数据
两种方式介绍
-
自己把JavaBean对象转换成json格式的字符串,响应给客户端
-
方法返回JavaBean对象,使用
@ResponseBody
注解Springmvc帮我们转换成json格式 【推荐】
前提条件
在pom.xml中导入依赖:jackson-databind
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
springmvc.xml中开启mvc注解开关
<mvc:annotation-driven/>
使用示例
package com.execise.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/*
返回json字符串
1. 手动把JavaBean和转化成json字符串,然后返回字符串
2. 直接返回javaBean即可
*/
@Controller
public class Controller05 {
// 手动把JavaBean对象转化成Json字符串,返回。
@ResponseBody
@RequestMapping(value = "/returnJson01" , produces = "application/json;charset=utf-8")
public String returnJson01() throws JsonProcessingException {
//1. 构建User
User user = new User("管理员01","xxx");
//2. 把这个对象手动转化成Json字符串
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(user);
return json;
}
/*
1. 直接返回JavaBean,springmvc会把这个JavaBean转化成json字符串返回。
2. 要想使用这种操作,那么必须要在pom.xml里面添加jackson-databind的依赖
*/
@ResponseBody
@RequestMapping("/returnJson02")
public User returnJson02() throws JsonProcessingException {
return new User("管理员02","xxx");
}
}
小结
如果要直接响应数据,使用response对象
public void method1(HttpServletResponse response){
//如果响应普通文本数据
//response.setContentType("text/html;charset=utf-8");
//如果响应json格式的字符串
response.setContentType("application/json;charset=utf-8");
response.getWriter().print("xxxx");
}
如果要bean对象 , 使用注解@ResponseBody
如果返回的是json数据,需要添加jackson-databind的依赖!
@RequestMapping("/method2")
@ResponseBody
public User method2(){
return new User();
}
四、常用注解
导入依赖
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!--jsp-api-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
</dependencies>
定义两个页面
index.jsp
<%--
Created by IntelliJ IDEA.
User: xiaomi
Date: 2021/9/10
Time: 8:58
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>
<h2>这是首页~!</h2>
</body>
</html>
success.jsp
<%--
Created by IntelliJ IDEA.
User: xiaomi
Date: 2021/9/10
Time: 8:58
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>
<h2>这是成功的页面~!</h2>
</body>
</html>
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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.execise"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:default-servlet-handler/>
</beans>
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_2_5.xsd"
version="2.5">
<!--配置前端控制器 DispatcherServlet-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--springmvc已经定义好了过滤器,可以帮助我们解决post请求,中文乱码的问题,我们只需要配置即可-->
<filter>
<filter-name>char</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置编码,通过初始化参数来设置编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>char</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
@RequestParam
作用:
-
可以对浏览器请求做出参数上的要求,要求一定要有指定名字的参数,如果没有,就报错!
-
可以把请求提交上来的参数赋值给方法的形参。
属性
value: 要求携带的参数名字
required:请求参数中是否必须提供此参数。 默认值: true。表示必须提供,如果不提供将报错。
defaultValue:默认值
使用示例
Controller01.java
package com.execise.controller;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/*
常用的注解:
*/
@Controller
public class Controller01 {
/*
@RequestParam:
作用: 要求客户端来访问的时候,一定要携带指定名称的参数,否则报错!
步骤:
1. 在方法参数的前面打注解 @RequestParam
属性:
name|value : 用来指定携带的参数名称,如果不指定,那么就以参数名为参考标准。
required: 表示是否一定要携带参数,默认值是true,
defaultValue: 默认值
*/
@RequestMapping("/requestParam")
public String requestParam(@RequestParam(value="abc" , required = false , defaultValue = "张三") String username , @RequestParam String password){
System.out.println("username = " + username);
System.out.println("password = " + password);
return "success";
}
}
@RequestParam 只能用于接收 url 的传参 ?name=xxx, form表单的提交。
无法接收提交的json数据(contentType=application/json)
@RequestBody
作用
-
用于获取请求体内容。 直接使用得到是 key=value&key=value…结构的字符串。【不常用】
-
把获得json类型的数据转成JavaBean对象(后面再讲)【推荐】
注意: get 请求方式不适用
属性
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值为 false, get 请求得到是 null。
-
@RequestBody 不能使用get请求, 在Controller的方法参数里,@RequestBody它只能出现一次!
-
匹配json数据的获取,例如:request.getInputStream()
使用实例
index.jsp 页面 追加以下内容
<h2>使用@RequestBody获取请求体:</h2>
<form action="/requestBody01" method="post">
用户名: <input type="text" name="username"/><br/>
密码: <input type="text" name="password"/><br/>
<input type="submit">
</form>
Controller01.java
package com.execise.controller;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/*
常用的注解:
*/
@Controller
public class Controller01 {
/*
@RequestBody
作用:
1. 可以获取表单提交上来的请求体,只有post请求才有请求体,get请求是没有请求体【不常用】
拿到的是一个字符串: username=admin&password=xxx;
2. 可以接收页面提交上来的json数据,封装到JavaBe里面【常用】
步骤:
1. 方法参数的前面打上注解 @RequestBody,springmvc就会把请求体赋值给方法的参数。
*/
@RequestMapping("/requestBody01")
public String requestBody01(@RequestBody String body) throws UnsupportedEncodingException {
System.out.println("body = " + body);
}
/*
页面提交中文的时候,默认情况下,浏览器会对内容进行编码,使用URLEncoder.encode("张三","utf-8");
6张三7 ----URLEncoder.encode("张三","utf-8")-----6%E5%BC%A0%E4%B8%897
如果我们希望看到正常的张三:
1. 必须要设置过滤器,真正的解决中文乱码的问题。
2. 对这份数据解码即可:
6%E5%BC%A0%E4%B8%897 ----------URLDecoder.decode("6%E5%BC%A0%E4%B8%897", utf-8);
*/
String data = URLDecoder.decode(body, "utf-8");
System.out.println("data = " + data);
return "success";
}
}
接收json数据
客户端发Ajax请求,提交json格式的数据
服务端接收json格式的数据,直接封装成User对象
前提条件
pom.xml中添加jackson的依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
springmvc.xml中,增加配置静态资源的处理
因为发请求,需要ajax的支持。
<!--配置静态资源的处理-->
<mvc:default-servlet-handler/>
需求实现
index.jsp 添加以下内容
<h2>使用@RequestBody来获取json数据</h2><input type="button" οnclick="sendJSON()" value="点我发送JSON数据"/><script src="js/axios-0.18.0.js"></script><script> function sendJSON(){ //1. 定义一份json数据 var data = {"username":"admin" , "password":"xxx"}; //2. 使用axios发起异步请求 axios.post("/requestBody02" , data); }</script>
Controller01
package com.execise.controller;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/*
常用的注解:
*/
@Controller
public class Controller01 {
/*
@RequestBody:
接收页面提交上来的json数据,封装到JavaBean对象身上。
步骤:
1. 前端页面必须提交的是一份json数据
2. 添加进来jackson-databind的依赖
如果不加依赖,会报错: 415 Unsupported Media Type
3. 定义一个JavaBean,属性名字不能乱写,必须和json里面的KEY一样。
4. 在Controller方法参数的前面加上注解@RequestBody
如果不打注解,那么得到都是null.
*/
@RequestMapping("/requestBody02")
public String requestBody02(@RequestBody User user ) {
System.out.println("user = " + user);
return "success";
}
}
@PathVariable
作用:
-
用于截取请求地址(url)里面的某些部分的数据。这个需要配合RestFul风格来说明
以前删除用户: localhost:82/deleteUser?id=3restFul : localhost:82/delete/3
属性:
-
value: 用于指定 url 中占位符名称。
-
required:是否必须提供占位符。
场景:获取路径中的参数,与restful编程风格一起,通常微服架构中使用
使用实例
index.jsp 页面添加以下内容
<h2>使用@PathVariable来获取地址中的参数</h2>
<a href="/delete/1">点我发请求</a>
<a href="/delete02/30/40">点我发请求2</a>
- Controller01.java
package com.execise.controller;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/*
常用的注解:
*/
@Controller
public class Controller01 {
/*
现在以 按照id来删除用户举例:
以前的写法: localhost:82/delete?id=1
restFul: localhost:82/delete/1
@PathVariable:
作用: 用来截取地址栏中的参数,赋值给方法的形参
步骤:
1. 在映射地址里面使用 {占位符名字} 来占位,表示匹配这个位置的数据
2. 在方法参数的前面加上注解 @PathVariable("占位的名字")
属性:
name | value : 用来指定|设置 占位符的名称。
如果name和value不写,只有一种情况可以不写,就是占位符的名字和方法的参数名字一样!
*/
@RequestMapping("/delete/{id}")
public String pathVariable(@PathVariable(value="id") int id) {
System.out.println("id = " + id);
return "success";
}
@RequestMapping("/delete02/{id}/{id2}")
public String pathVariable02(@PathVariable int id , @PathVariable int id2) {
System.out.println("id = " + id);
System.out.println("id2 = " + id2);
return "success";
}
}
@RequestHeader
作用:
- 获取指定名字的请求头数据,赋值给方法参数。
属性:
-
value:提供消息头名称
-
required:是否必须有此消息头 , 默认值是true
-
从请求头中获取参数,鉴权(token 畅购open auth 2.0 jwt token) Authorization
使用实例
index.jsp 页面 添加以下内容
<h2>使用@ReqeustHeader来获取请求头的数据</h2>
<a href="/requestHeader">点我发请求</a>
Controller01.java
package com.execise.controller;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/*
常用的注解:
*/
@Controller
public class Controller01 {
/*
@RequestHeader:
作用: 用来获取指定名称的请求头数据,赋值给方法的参数
*/
@RequestMapping("/requestHeader")
public String requestHeader(@RequestHeader("User-Agent") String data) {
System.out.println("data = " + data);
return "success";
}
}
@CookieValue
作用:
- 用于把指定 cookie 名称的值传入控制器方法参数。
属性:
-
value:指定 cookie 的名称。
-
required:是否必须有此 cookie, 默认值是:true
使用实例
index.jsp 页面添加以下内容
<h2>使用@CookieValue来获取Cookie的值</h2>
<a href="/cookieValue">点我发请求</a>
Controller01.java
package com.execise.controller;
import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/*
常用的注解:
*/
@Controller
public class Controller01 {
/*
@CookieValue
作用: 用来获取指定名称的cookie的值,赋值给方法的形参。
属性:
name&value : 指定cookie的名字!
JESSIONID:
1. 这是session作用域的id的名称,它会存放到cookie里面去。
2. 当我们打开首页的时候,访问的是index.jsp ----背后会被翻译成========> Servlet
3. 在这个翻译成的servlet里面,它会创建session对象,并且把session的地址存放到cookie里面去。
存放的cookie的KEY 叫做: JSESSIONID.
*/
@RequestMapping("/cookieValue")
public String cookieValue(@CookieValue("JSESSIONID") String data) {
System.out.println("data = " + data);
return "success";
}
}
小结
-
这几个注解都使用作用在方法的参数上,不是写在方法上。他们或多或少都是对客户端提交的数据有这样或者那样的要求
-
@RequestParam : 要求客户端必须要携带指定的参数。
-
@RequestBody: 要求必须有请求体,一般它是作用于 页面提交上来的json数据,转化成 javabean对象
-
@PathVariable : 路径变量,配合RestFul风格使用,用于截取地址里面的数据
-
@RequestHeader: 用来获取的指定的请求头数据,赋值给方法形参
-
@CookieValue: 用来获取的cookie数据,赋值给方法的形参。