这里重新推荐下载安装Tomcat进行配置的方法:
第一步:安装Tomcat,可以不用看里面安装JDK的部分,毕竟都学到SpringMVC了,JDK肯定也安装了:Tomcat安装和配置,超详细(附JDK安装及配置环境变量)
第二步:在IDEA里面配置Tomcat运行环境:SpringMVC——配置tomcat
第三步:修改一下下面两个位置,使得我们通过/
就可以访问,否则如果这个链接后面带了一堆乱七八糟的东西,我们访问localhost/
会报404的错误:
注意,启动端口设置成了80,localhost
等价于localhost:80
PostMan的作用就是模仿浏览器发出一些请求,比如Get
请求、Post
请求,如果在Post请求我们还需要自己写一个表单去提交,ajax请求的时候还需要些json,通过PostMan就可以解决以下这些问题
创建了WorkSpace后我们也可以随时切换不同的WorkSpace:
输入请求地址,得到响应内容:
点击preview的话就可以看到网页预览的结果,比如我们请求一下百度:
其他的话就是点击右上角的save
可以将请求保存到左侧,方便后续测试使用
config/SpringMvcConfig.java
package com.demo.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.demo.controller")
public class SpringMvcConfig {
}
config/ServletContainerInitConfig.java
package com.demo.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
controller/BookController.java
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BookController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("book save ...");
return "{'module': 'book save'}";
}
}
controller/UserController.java
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("User save...");
return "{'module': 'springmvc'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(){
System.out.println("User delete...");
return "{'module': 'springmvc'}";
}
}
因为在BookController和UserController下有相同的路径,就会导致报错
团队多人开发,每个人设置不同的请求路径,应该怎么解决?
各个Controller用不同的路径
在UserController.java中带上/user
前缀:
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/user/save")
@ResponseBody
public String save(){
System.out.println("User save...");
return "{'module': 'user save'}";
}
@RequestMapping("/user/delete")
@ResponseBody
public String delete(){
System.out.println("User delete...");
return "{'module': 'user delete'}";
}
}
在BookController.java中带上/book
前缀:
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BookController {
@RequestMapping("/book/save")
@ResponseBody
public String save(){
System.out.println("book save ...");
return "{'module': 'book save'}";
}
}
再次启动,问题解决,测试一下/user/save
和/book/save
:
在Controller上定义请求模块的前缀,在每一个Controller上加上对应的前缀,如下
在UserController.java模块顶部加上/user
前缀:
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("User save...");
return "{'module': 'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(){
System.out.println("User delete...");
return "{'module': 'user delete'}";
}
}
在BookController.java模块顶部加上/book
前缀:
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("book save ...");
return "{'module': 'book save'}";
}
}
注解:@RequestMapping
类型:方法注解、类注解
位置:Spring控制器方法定义上方
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀
两种请求的传参方式是不一样的,在这里理解在Get请求和Post请求中如何传参
config/ServletContainerInitConfig.java
package com.demo.config;
import org.apache.ibatis.jdbc.Null;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
config/SpringMvcConfig.java
package com.demo.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.demo.controller")
public class SpringMvcConfig {
}
controller/UserController.java
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(){
return "{'module': 'common param'}";
}
}
domain/User.java:
package com.demo.domain;
public class User {
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
domain/Address.java:
package com.demo.domain;
public class Address {
private String province;
private String city;
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
一般Get请求是通过在链接后面接上?
,然后再写具体的参数名称和内容,例如:localhost/commonParam?name=aaa
接收参数的方法就是在方法上加上对应的形参:
package com.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name){
System.out.println("接收到的参数name==>" + name);
return "{'module': 'common param'}";
}
}
此时除了可以看到返回的结果,还可以在控制台看到打印的参数:
如果是多个参数,就修改上面代码的方法处,用多个参数进行接收即可:
public String commonParam(String name, int age)
再测试,结果如下:
PS: PostMan小技巧:如果不需要这个参数,就把前面的勾去掉,若彻底不再需要这个参数,在参数那一栏上的最右侧会有一个小垃圾桶,点击去除
无论是Get请求还是Post请求,对于后台请求的代码来说没有变化,这里和Servlet不一样,Servlet中区分doGet
和doPost
,在这里合二为一了
在Postman中,我们如下测试,注意,在Post请求中不是在Param
中写参数,而是在Body
中写参数,Body
下的x-www-form-urlencoded
位置加上参数:
假如我们在Post请求中给name参数的是中文,在运行时候会发现接收到的参数乱码:
为了解决这个问题,我们就需要在
ServletContainerInitConfig
中设置一个过滤器:
package com.demo.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
如果是Get请求,如上配置后出来的还会是乱码(看弹幕有人说Tomcat8及以上设置了过滤器后Get请求出来的会是中文而不是乱码,如果使用本篇文章第0点中的方式来配置Tomcat,确实不会出现乱码的问题)
但是之前我是使用在pom.xml中通过配置来进行启动服务的配置,那时候我是参考了这一篇博客来设置GET请求不乱码:SpringMVC 中如何解决POST和GET请求中文乱码问题?(面试题)
修改pom.xml中的build配置如下,加上了uriEncoding
的配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.1version>
<configuration>
<port>80port>
<path>/path>
<uriEncoding>UTF-8uriEncoding>
configuration>
plugin>
plugins>
build>
再次执行GET请求,结果如下:
注意:只设置下面这种而不写过滤器,是不能保证Post请求不乱码的
大概查了一下网上的资料,我认为一个请求不能满足两种需求的原因是:因为Post请求会把数据放到正文中传递,然后过滤器是针对于这个正文来设定的,然后我们配置的build是针对于得到的链接来进行设定的
在controller/UserController.java中加上一个方法:
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(String username, int age){
System.out.println("接收到的参数name==>" + username);
System.out.println("接收到的参数age==>" + age);
return "{'module': 'common param different name'}";
}
使用不同名称的参数,则username就无法接收到:
如果想要使方法能够接收不同名称的参数,就使用注解:@RequestParam
,如下:
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String username, int age){
System.out.println("接收到的参数name==>" + username);
System.out.println("接收到的参数age==>" + age);
return "{'module': 'common param different name'}";
}
在controller/UserController.java中加上一个方法:
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("接收带的参数user==>" + user);
return "{'module': 'pojo param'}";
}
在测试的时候带上name和age属性,框架可以帮我们自动封装到实体类中:
在controller/UserController.java中加上一个方法:
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> " + user);
return "{'module': 'pojo contain pojo param'}";
}
在domain/User.java中加上一个Address属性,加上getter和setter方法,更新toString()方法:
package com.demo.domain;
public class User {
private String name;
private int age;
Address address;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在controller/UserController.java中加上一个方法:
@RequestMapping("/arrayParam")
@ResponseBody
public String stringParam(String[] likes){
System.out.println("数组参数传递 likes ==> " + Arrays.toString(likes));
return "{'module': 'array param'}";
}
在controller/UserController.java中加上一个方法:
@RequestMapping("/listParam")
@ResponseBody
public String listParam(List<String> likes){
System.out.println("集合参数传递 likes ==> " + likes);
return "{'module': 'list param'}";
}
如果使用像第四种那样的方式来进行传参,会报错java.lang.NoSuchMethodException: java.util.List.
,显示没有这样的init方法
这是因为List
被当成pojo参数处理了。因为List
被视作了一个引用类型对象,而框架对于引用类型对象的处理是:先通过构造方法构造一个对象,然后通过set
方法将属性set
到这个对象里面去,所以这里会报错。
为了避免这个问题,我们需要修改这个方法中的参数,简单地加一下@RequestParam
,让框架将其作为一个参数进行处理,而不是作为一个引用对象:
public String listParam(@RequestParam List<String> likes)
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.14.2version>
dependency>
在controller/SpringMvcConfig.java中加上一个注解@EnableWebMvc
当然,这个注解能够实现的不止这个功能,该注解整合了多个功能,不止解析JSON这一个功能
package com.demo.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.demo.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
在controller/UserController.java中加上一个方法:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(List<String> likes){
System.out.println("list common(json)参数传递 list ==> " + likes);
return "{'module': list common for json param}";
}
对于被封装起来的JSON,我们在这里换一个注解:@RequestBody
即可:public String listParamForJson(@RequestBody List
,测试如下:
在controller/UserController.java中加上一个方法,同理,为了解析JSON,需要加上@RequestBody
:
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==> " + user);
return "{'module': pojo for json param}";
}
结果如下:
因为没有给address的信息,所以address=null,进一步改进一下:
在controller/UserController.java中加上一个方法,类似的:
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
System.out.println("list pojo(json)参数传递 user ==> " + list);
return "{'module': list pojo for json param}";
}
类型:这个参数是一个形参注解
位置:定义位置在SpringMVC控制器方法形参定义前面
作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
区别
@RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
@RequestBody用于接收json数据【application/json】
应用
后期开发中,发送json格式数据为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
在controller/UserController.java中加上一个方法:
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date){
System.out.println("参数传递 date ==> " + date);
return "{'module': data param}";
}
测试一下,使用2088/08/08
这种斜杠式是可以被解析的
在参数里面加上date1,打开上图的date1数据再测试,会报错无法解析这种格式:
这里应该使用注解@DateTimeFormat
注解来指定格式:
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date, @DateTimeFormat(pattern = "yyyy-MM-dd") Date date1){
System.out.println("参数传递 date ==> " + date);
System.out.println("参数传递 date1 ==> " + date1);
return "{'module': data param}";
}
如果需要加上时间,就使用格式:@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss)"
即可:
Converter接口(其实我没找到这个)
public interface Converter<S, T>{
@Nullable
T convert(S var1);
}
请求参数年龄数据(String→Integer)
日期格式转换(String→Date)
在controller/UserController.java中加上一个方法:
@RequestMapping("/toJumpPage")
public String toJumpPage(){
System.out.println("跳转页面");
return "index.jsp";
}
在controller/UserController.java中加上一个方法:
@RequestMapping(path="/toJumpPage")
public String toJumpPage(){
System.out.println("跳转页面");
return "index.jsp";
}
webapp/index.jsp中的内容:
<html>
<body>
<h2>Hello Spring MVC!h2>
body>
html>
在浏览器中输入localhost/toJumpPage
得到以下结果:
这说明Controller中写方法,会自动帮我们找返回的这个String所对应的页面。
在controller/UserController.java中加上一个方法,如果不用@ResponseBody
,则方法会尝试去找一个叫做/response text
的页面:
@RequestMapping("/toText")
@ResponseBody
public String toText(){
System.out.println("返回纯文本数据");
return "response text";
}
默认返回的是一个String,我们想让框架帮我们把User转换成JSON传递过去,其实就是需要修改响应的内容,响应的不再是页面,而需要我们自定义,则需要加上@ResponseBody
注解
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
System.out.println("返回json对象数据");
User user = new User();
user.setName("啊啊啊");
user.setAge(15);
return user;
}
@RequestMapping("/toJsonPOJOList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回JSON集合数据");
User user1 = new User();
user1.setName("aaa");
user1.setAge(33);
User user2 = new User();
user2.setName("bbb");
user2.setAge(22);
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
return userList;
}
这是一个方法注解,位置位于SpringMVC控制器方法定义上方
作用:设置当前控制器返回值作为响应体