1 SpringMVC简介
1.1 SpringMVC与Servlet的比较
- Servlet写法
- 代码
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
@WebServlet("/user/save")
public class UserDeleteServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException{
//1.接收请求参数并打印
//html name = "123"
String name = req.getParameter("name");
System.out.println("servlet save name ==>"+name);
//2.产生相应
resp.setContentType("text/json;charset=utf-8");
//获取输出流
PrintWriter pw = resp.getWriter();
pw.write("{'module':'servlet save'}");
}
@Override
protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException{
//doPost方法转调doGet方法
this.doGet(req,resp);
}
//常用,get和post都可以用
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
}
- 结果
- SpringMVC写法
- 总体层次
- 代码
package com.itheima.springmvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
//定义表现层控制器bean
@Controller
public class UserController {
//设置映射路径为/save,即外部访问路径
@RequestMapping("/save")
//设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
//设置映射路径为/delete,即外部访问路径
@RequestMapping("/delete")
@ResponseBody
public String delete(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
}
- 结果
在这里插入图片描述
- 总结
使用MVC代码更加简洁,如果请求个数多,那么接收参数的这一行 String name = req.getParameter(“name”)需要多个
MVC直接在形参上接收,不需要写那么多行
1.2 SpringMVC概述
- 流程
- 概念
Spring是一种基于Java实现MVC模型的轻量级Web框架
1.3 SpringMVC 入门案例
- 导入pom依赖jar包
- 依赖如下
<dependencies>
<!-- 导入坐标springmvc和servlet的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 怕和插件tomcat有冲突,因此配成provided-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
- 创建Spring控制器类
使用@Controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
//1.定义bean
@Controller
public class UserController {
//2.1设置当前的访问路径
@RequestMapping("/save")
//2.2设置当前操作的返回值类型
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'springmvc'}";
}
}
- Spring的配置类
专门用于SpringMvcConfig
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//目的是加载Controller的bean,是一个配置类
//3.创建springmvc的配置文件,然后加载controller对应的bean
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
- 专门配置类加载MVC环境
Tomcat启动后 ,使用ServletContainsInitConfig用于识别SpringMVC
WebApplicationContext方法是识别MVC
getServletMappings方法是,哪一些请求给到MVC处理
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
//4.定义一个Servlet容器启动的配置类。在里面加载spring的配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
//原来用这个AnnotationConfigApplicationContext
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//register去架子啊spring的配置
ctx.register(SpringMvcConfig.class);
return ctx;
}
//设置哪些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
//表示所有请求归于springmvc处理
return new String[]{"/"};
}
//加载spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
- 启动
- run起来
- 多个请求也可以
1.4 Bean加载控制
- 场景
springmvc是加载controller
spring加载service或者dao或者datasource或者mybatis
难点在于:怎么控制spring不去加载controller(springmvc加载的)
- 方法
方法一:spring加载的时候排除掉springmvc加载的
方法二:spring加载扫描的变成精确扫描
- 结构
- 代码
- UserDao接口
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Insert;
public interface UserDao {
@Insert("insert into tbl_user(name,age)values(#{name},#{age})")
public void save(User user);
}
- service层的接口
import com.itheima.domain.User;
public interface UserService {
public void save(User user);
}
- service层的实现类
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
}
}
- 配置类
就是在这边进行方法的解决
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
//目的是加载Controller的bean,是一个配置类
//3.创建springmvc的配置文件,然后加载controller对应的bean
@Configuration
//方法二:指定所有的扫描范围,一定要写扫描dao包,目的是读写数据库
//@ComponentScan({"com.itheima.service","com.itheima.dao"})
//方法一:扫描it黑马下所有的东西了,但是排除
@ComponentScan(value="com.itheima",
excludeFilters = @ComponentScan.Filter(
//按照注解过滤
type = FilterType.ANNOTATION,
//如果一个类中写了@Controller那么就过滤掉
classes = Controller.class
)
)
public class SpringConfig {
}
- 注释掉springmvc的配置SpringMvcConfig类
因为即使SpringConfig过滤掉了,SpringMvcConfig这个也会把UserController加载回来的
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
- 测试启动类App
import com.itheima.config.SpringConfig;
import com.itheima.controller.UserController;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//拿到SpringConfig的配置
ctx.register(SpringConfig.class);
//打印UserController看看有没有被过滤掉
System.out.println(ctx.getBean(UserController.class));
}
}
- 其他
pom.xml配置中加入mybatis依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
实体类domain层中有个User类
package com.itheima.domain;
public class User {
private Integer id;
private String name;
private Integer age;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
- 结果
- 排除
当SpringConfig为排除时,App启动后时获取不到UserController类,报出NoSuchBeanDefinitionException的错误
@Configuration
//方法二:指定所有的扫描范围,一定要写扫描dao包,目的是读写数据库
//@ComponentScan({"com.itheima.service","com.itheima.dao"})
//扫描it黑马下所有的东西了,但是排除
@ComponentScan(value="com.itheima",
excludeFilters = @ComponentScan.Filter(
//按照注解过滤
type = FilterType.ANNOTATION,
//如果一个类中写了@Controller那么就过滤掉
classes = Controller.class
)
)
public class SpringConfig {
}
- 不排除
不排除就能获取到UserController类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
//目的是加载Controller的bean,是一个配置类
//3.创建springmvc的配置文件,然后加载controller对应的bean
@Configuration
//方法二:指定所有的扫描范围,一定要写扫描dao包,目的是读写数据库
//@ComponentScan({"com.itheima.service","com.itheima.dao"})
//扫描it黑马下所有的东西了,但是排除
@ComponentScan(value="com.itheima"
// excludeFilters = @ComponentScan.Filter(
// //按照注解过滤
// type = FilterType.ANNOTATION,
// //如果一个类中写了@Controller那么就过滤掉
// classes = Controller.class
// )
)
public class SpringConfig {
}
- ServletContainersInitConfig类
去继承AbstractAnnotationConfigDispatcherServletInitializer类简化既要加载spring配置又要springmvc配置的情况
package com.itheima.config;
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 ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
4.定义一个Servlet容器启动的配置类。在里面加载spring的配置
//public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//
// //加载springMVC容器配置
// @Override
// protected WebApplicationContext createServletApplicationContext() {
// //原来用这个AnnotationConfigApplicationContext
// AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// //register去找springmvc的配置
// ctx.register(SpringMvcConfig.class);
// return ctx;
// }
// //加载spring容器配置
// //把spring的SpringConfig也加进来
// protected WebApplicationContext createRootApplicationContext() {
// AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// ctx.register(SpringConfig.class);
// return ctx;
// }
//
// //设置哪些请求归属springMVC处理
// @Override
// protected String[] getServletMappings() {
// //表示所有请求归于springmvc处理
// return new String[]{"/"};
// }
//}
1.5 Postman使用
- 创建
- 发送请求
- 保存请求以便后续继续测试
下次直接点保存的请求名字,send就能用了
2 请求与响应
2.1 设置请求映射路径
- 场景
如果多个Cotroller中都有/save,启动的时候,springmvc就是不知道会去调用谁
- 解决
通过设置模块名作为请求路径前缀
@RequestMapping
例如/user/save;/book/save
- 代码
- 层次
- 代码
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'}";
}
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
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'}";
}
}
- 结果
2.2 get和post请求发送普通参数
- 结构
- get请求
- 基本传参
@Controller
public class UserController {
//普通参数:请求参数与形参名称对应即可完成参数传递
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name){
System.out.println("普通参数传递 name ==> "+name);
return "{'module':'common param'}";
}
}
- 两个参数
@Controller
public class UserController {
//普通参数:请求参数与形参名称对应即可完成参数传递
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
System.out.println("普通参数传递 name ==> "+name);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param'}";
}
}
- post请求
post请求需要在postman的body中编辑请求体
3. 中文参数设置
在ServletContainersInitConfig类中增加乱码处理,只针对post,不针对get
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
2.3 5种类型参数传递
- 请求参数名和方法中的形参不一样
- 使用@RequestParam
//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
System.out.println("普通参数传递 userName ==> "+userName);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param different name'}";
}
- POJO参数
- 代码
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
- User实体类
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;
}
}
- 嵌套POJO参数
- 实体类User实体类嵌套了Address实体类
public class User {
private String name;
private int age;
private 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;
}
}
- 代码
//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
return "{'module':'pojo contain pojo param'}";
}
- 测试
嵌套的用address.city
- 数组参数
- 代码
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
return "{'module':'array param'}";
}
- 集合参数
- 代码
通过@RequestParam
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'module':'list param'}";
}
- 结果
2.4 json数据传参
- postman接收json数据
- 开启配置
使用@EnableWebMvc开启json数据类型自动转换
@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
- 代码
- json
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//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'}";
}
- keyvalued的json
//POJO参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//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'}";
}
嵌套
- list中嵌套实体类
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//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'}";
}
2.5 日期型参数传递
先欠着,优先级次级
2.6 响应
- 响应页面
//响应页面/跳转页面
//返回值为String类型,设置返回值为页面名称,即可实现页面跳转
@RequestMapping("/toJumpPage")
public String toJumpPage(){
System.out.println("跳转页面");
return "page.jsp";
}
- 响应文本数据
@ResponseBody表示返回字符串
//响应文本数据
//返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
@RequestMapping("/toText")
@ResponseBody
public String toText(){
System.out.println("返回纯文本数据");
return "response text";
}
- 响应POJO对象
@ResponseBody将POJO对象转成Json
//响应POJO对象
//返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
System.out.println("返回json对象数据");
User user = new User();
user.setName("itcast");
user.setAge(15);
return user;
}
- 响应POJO集合对象
@ResponseBody将List转成Json
//响应POJO集合对象
//返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("传智播客");
user1.setAge(15);
User user2 = new User();
user2.setName("黑马程序员");
user2.setAge(12);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
return userList;
}
- 需要的依赖
都是有了jakeson包才可以使用@ResponseBody进行转换
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
3 REST风格
3.1 REST简介
- 传统风格
- REST风格描述形式
- REST风格参照表
3.2 RESTful入门案例
- POST
- 原来
- 修改Rest
使用method去限制
//设置当前请求方法为POST,表示REST风格中的添加操作
@RequestMapping(value = "/users",method = RequestMethod.POST)
@ResponseBody
public String save(){
System.out.println("user save...");
return "{'module':'user save'}";
}
- DELETE
- 原来
- 修改
value = "/users/{id},其中的id表示参数
@PathVariable使用路径变量,从路径中取值
//设置当前请求方法为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
- 原来
- 修改
//设置当前请求方法为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
- 原来
- 修改
//设置当前请求方法为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'}";
}
3.3 RESTful快速开发
- 简介
- 原来
- 修改
- 代码
- 原来
- 修改
- 再修改
- 再简化