package servlet;
@WebServlet("/user/save")
public class UserSaveServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 接收请求参数
String name = req.getParameter("name");
//2. 产生响应
resp.setContentType("text/json;charset=utf-8");
PrintWriter pw = resp.getWriter();
pw.write("UserSaveServlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
启动:mvn tomcat7:run
servlet问题之一:如果要接收多个参数,要么将它们抽取成一个实体类,要么多行getParameter
SpringMVC可以实现Servlet一样的功能,但更加简便
需要javaweb前置知识
MVC是指表现层,业务层(service),数据层(dao)
之前的表现层是用servlet实现,现在学习通过SpringMVC来实现表现层
<dependencies>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
plugin>
plugins>
build>
注意,这里可以设置项目启动的端口和路径
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
这样设置后对应的访问路径就如:http://localhost/save
package org.example.controller;
//2. 定义Controller
//2.1 使用@Controller定义bean
@Controller
public class UserController {
//2.2 设置当前操作的访问路径
@RequestMapping("/save")
//2.3 设置当前操作的返回值类型
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'springmvc'}";
}
}
package org.example.config;
//3. 创建SpringMVC的配置文件
@Configuration
@ComponentScan("org.example.controller")
public class SpringMvcConfig {
}
将web.xml删除,换成ServletContainersInitConfig
package org.example.config;
//4. 定义一个servlet容器启动的配置类,在里面加载spring配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
//设置哪些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; //所有请求都归SpringMVC处理
}
//加载spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;//暂时不用
}
}
重点:
启动:mvn tomcat7:run
访问:http://localhost:8080/springmvc/save
名称 | @Controller |
---|---|
类型 | 类注解 |
位置 | SpringMVC控制器类定义上方 |
作用 | 设定SpringMVC的核心控制器bean |
名称 | @RequestMapping |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
spring-webmvc
jar包的原因是它会自动依赖spring相关坐标AbstractDispatcherServletInitializer
类是SpringMVC提供的快速初始化Web3.0容器的抽象类createServletApplicationContext
方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围getServletMappings
方法,设定SpringMVC对应的请求映射路径,即SpringMVC拦截哪些请求createRootApplicationContext
方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式和createServletApplicationContext相同。服务器启动,执行ServletContainersInitConfig类,初始化web容器
执行createServletApplicationContext方法,创建了WebApplicationContext对象
加载SpringMvcConfig配置类
执行@ComponentScan加载对应的bean
加载UserController,每个@RequestMapping的名称对应一个具体的方法
/save
和 save方法的对应关系执行getServletMappings方法,设定SpringMVC拦截请求的路径规则
protected String[] getServletMappings() {
return new String[]{"/"}; //所有请求都归SpringMVC处理
}
/
代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
http://localhost:8080/springmvc/save
@RequestMapping
定义的映射关系,由/save匹配执行对应的方法save()@ResponseBody
直接将save()方法的返回值作为响应体返回给请求方概述
contrller、service和dao这些类都需要被容器管理成bean对象,那么到底是该让SpringMVC加载还是让Spring加载呢?
排除bean的方法
为了避免Spring加载到SpringMVC的bean,需要在加载Spring控制的bean时,排除掉SpringMVC控制的bean
SpringMVC相关bean的加载控制
Spring相关bean的加载控制
方式一:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
package org.example.config;
@Configuration
@ComponentScan({"org.example.service", "org.example.dao"})
public class SpringConfig {
}
使用mybatis自动代理时,这里的dao包是否可以不扫描?
方式二:Spring加载的bean设定扫描范围为org.example,排除掉controller包中的bean
package org.example.config;
@Configuration
@ComponentScan(value = "org.example",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION, //设置按照注解过滤
classes = Controller.class //过滤的注解类型是Controller
))
public class SpringConfig {
}
excludeFilters
:排除扫描路径中加载的bean
includeFilters
:加载指定的bean
方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中
测试
先将SpringMvcConfig里面的@ComponentScan注释掉,避免SpringMVC加载Controller的bean
此时就只剩下Spring会加载bean
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);//也可以通过register的方式
System.out.println(ctx.getBean(UserController.class));
}
结果发现getBean无法获取到UserController对象,说明该bean没有被加载
PostMan是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件,常用于进行接口测试
下载安装
https://www.postman.com/downloads/
双击下载好的应用,无需安装,根据提示注册账号即可进入postman
创建一个新的workspace
发送请求
保存请求地址
为了避免重名,每一个模块应该添加一个请求路径前缀
package org.example.controller;
@Controller
@RequestMapping("/book")//设置请求路径的前缀
public class BookController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("book save ...");
return "{'module':'book save'}";
}
}
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
//普通参数
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name, int age){
System.out.println("普通参数传递:name="+name+",age="+age);
return "{'module':'common param'}";
}
}
在ServletContainersInitConfig配置类中重写getServletFilters
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
注意这样做只能处理post请求的中文乱码,get需要先编码再解码(之后补充)
当请求里面的参数名称和后台函数中的参数名称不一致时,可以通过@RequestParam("name")
注解来解决
public String commonParam(@RequestParam("name") String userName, @RequestParam("age") int age){
System.out.println("普通参数传递:userName="+userName+",age="+age);
return "{'module':'common param'}";
}
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
//POJO参数
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递:user="+user);
return "{'module':'pojo param'}";
}
}
在postman中,无论是get方式还是post方式,发送实体类参数方法是和发送普通参数一样的,它将自动和User类里面的属性匹配
post方法同理
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递:likes="+ Arrays.toString(likes));
return "{'module':'array param'}";
}
}
post方法同理
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes) {
System.out.println("集合参数传递:likes=" + likes);
return "{'module':'list param'}";
}
}
此时再按照array的方法发出请求即可。如果不加RequestParam
,likes将被当成一个对象初始化,然而List是接口不是对象,因此报错
步骤1:导入json坐标
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
步骤2:开启SpringMVC注解支持
SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能
package org.example.config;
@Configuration
@ComponentScan("org.example.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
//普通集合参数: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'}";
}
}
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
//POJO参数:json格式
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递:user="+user);
return "{'module':'pojo for json param'}";
}
}
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
//对象集合参数
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> users){
System.out.println("list pojo(json)参数传递:users="+users);
return "{'module':'list pojo for json param'}";
}
}
区别
应用
默认情况下
//日期参数
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(Date date){
System.out.println("日期参数传递:date="+date);
return "{'module':'date param'}";
}
发送请求
http://localhost:8080/springmvc/user/dateParam?date=2022/08/06
获取到的date为
Sat Aug 06 00:00:00 CST 2022
设置日期格式:@DateTimeFormat
//日期参数
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
System.out.println("日期参数传递:date="+date);
return "{'module':'date param'}";
}
发送请求
http://localhost:8080/springmvc/user/dateParam?date=2022-08-06
获取到的date为
Sat Aug 06 00:00:00 CST 2022
添加时间属性
//日期参数
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date){
System.out.println("日期参数传递:date="+date);
return "{'module':'date param'}";
}
发送请求
http://localhost:8080/springmvc/user/dateParam?date=2022-08-06 8:08:08
获取到的date为
Sat Aug 06 08:08:08 CST 2022
考虑以下情况:
后台需要的数据类型有很多种,在数据的传递过程中存在很多类型的转换,那么谁来做这个类型转换?
SpringMVC中提供了很多类型转换接口和实现类,例如Convert接口
Convert接口
package org.springframework.core.convert.converter;
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S var1);
}
用来实现不同数据类型之间的转换,如:
请求参数年龄数据(String→Integer)
日期格式转换(String → Date)
@EnableWebMvc:功能之一就是根据类型匹配对应的类型转换器
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
package org.example.controller;
@Controller
@RequestMapping("/user")
public class UserController {
//响应页面/跳转页面
@RequestMapping("/toJumpPage")
public String toJumpPage(){
System.out.println("跳转界面");
return "/page.jsp";
}
//响应文本数据
@RequestMapping("/toText")
@ResponseBody
public String toText(){
System.out.println("返回纯文本数据");
return "responce text";
}
//响应POJO数据
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
System.out.println("返回json对象数据");
User user = new User();
user.setName("zhangsan");
user.setAge(15);
return user;
}
//响应POJO集合对象
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回json集合数据");
List<User> userList = new ArrayList<User>(){{
add(new User("zhangsan", 15));
add(new User("lisi", 16));
}};
return userList;
}
}
在postman中发送形如:http://localhost:8080/springmvc/user/toJsonList,即可看到结果
名称 | @ResponseBody |
---|---|
类型 | 方法\类注解 |
位置 | SpringMVC控制器方法定义上方和控制类上 |
作用 | 设置当前控制器返回值作为响应体, 写在类上,该类的所有方法都有该注解功能 |
相关属性 | pattern:指定日期时间格式字符串 |
说明:
写在类上就是该类下的所有方法都有@ReponseBody功能
当方法上有@ReponseBody注解后
内部通过一个新的接口HttpMessageConverter
实现
HttpMessageConverter接口 - 类型转换器
实现json类型转换
传统风格资源描述形式
REST风格描述形式
REST的优点有:
REST风格访问资源时使用行为动作
区分对资源进行了何种操作
http://localhost/users
查询全部用户信息 GET
(查询)http://localhost/users/1
查询指定用户信息 GET
(查询)http://localhost/users
添加用户信息 POST
(新增/保存)http://localhost/users
修改用户信息 PUT
(修改/更新)http://localhost/users/1
删除用户信息 DELETE
(删除)按照不同的请求方式代表不同的操作类型
根据REST风格对资源进行访问称为RESTful
增删改查代码
package org.example.controller;
@Controller
public class UserController {
//设置当前请求方法为POST,表示REST风格中的添加操作
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(){
System.out.println("user save...");
return "{'module':'user save'}";
}
//设置当前请求方法为DELETE,表示REST风格中的删除操作
//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
//设置当前请求方法为PUT,表示REST风格中的修改操作
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user){
System.out.println("user update..."+user);
return "{'module':'user update'}";
}
//设置当前请求方法为GET,表示REST风格中的查询操作
//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id){
System.out.println("user getById..."+id);
return "{'module':'user getById'}";
}
//设置当前请求方法为GET,表示REST风格中的查询操作
@RequestMapping(value = "/users", method = RequestMethod.GET)
@ResponseBody
public String getAll(){
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
}
发送请求的方式分别为
1. 增:save()
POST http://localhost:8080/springmvc/users
2. 删:delete(Integer id)
DELETE http://localhost:8080/springmvc/users/1
3. 改:update(User user)
PUT http://localhost:8080/springmvc/users
4. 查:getById(Integer id)
GET http://localhost:8080/springmvc/users/1
5. 查:getAll()
GET http://localhost:8080/springmvc/users
接收参数有三个注解@RequestBody
、@RequestParam
、@PathVariable
package org.example.controller;
@RestController //等同于 @Controller 和 @ResponseBody 两个注解组合功能
@RequestMapping("/books")
public class BookController {
//@RequestMapping(method = RequestMethod.POST)
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
//@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
//@RequestMapping(method = RequestMethod.PUT)
@PutMapping
public String update(@RequestBody Book book){
System.out.println("book update..."+book);
return "{'module':'book update'}";
}
//@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("book getById..."+id);
return "{'module':'book getById'}";
}
//@RequestMapping(method = RequestMethod.GET)
@GetMapping
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
RESTful风格的访问方式不变
需求:
这里没有做实际数据库操作,只是用了假数据
<dependencies>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>80port>
<path>/path>
configuration>
plugin>
plugins>
build>
配置类需要ServletContainersInitConfig,SpringMvcConfig,SpringConfig,配置代码见1.2节
package org.example.controller;
@RestController
@RequestMapping("/books")
public class BookController {
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save ==> " + book);
return "{'module':'book save success'}";
}
@GetMapping
public List<Book> getAll(){
List<Book> bookList = new ArrayList<>();
Book book1 = new Book();
book1.setType("计算机");book1.setName("SpringMVC入门教程");book1.setDescription("小试牛刀");
bookList.add(book1);
Book book2 = new Book();
book2.setType("计算机");book2.setName("SpringMVC实战教程");book2.setDescription("一代宗师");
bookList.add(book2);
return bookList;
}
}
新增图书
列表查询
将前端页面拷贝到项目中去
此时访问:http://localhost/pages/books.html,将报错
原因:在ServletContainersInitConfig中重写getServletMappings时,设置了拦截所有资源,因此报错 No mapping for GET /pages/books.html
解决方法:放行
SpringMvcSupport
package org.example.config;
//用于放行前端页面
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/下的资源时,走/pages目录下的内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
SpringMvcConfig
package org.example.config;
@Configuration
@ComponentScan({"org.example.controller", "org.example.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
前端发送ajax请求(了解):
methods: {
...
//添加
saveBook() {
axios.post("/books", this.formData).then((res) => {
});
},
//主页列表查询
getAll() {
axios.get("/books").then((res) => {
this.dataList = res.data;
});
},
...
}