SpringMVC
- SpringMVC 技术与 Servlet 技术功能相同,均属于Web层开发技术
- SpringMVC 是一种基于Java实现的MVC模型的轻量级Web框架
- 相较于 Servlet 使用简单,开发便捷,灵活性强。
由于目前绝大多数项目都是使用Springboot框架开发,所以本文只讲述一些重要概念,而不去实现。
案例
第一步:导坐标
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
第二步:创建SpringMVC控制器类
等同于Servlet功能
@Controller //声明这个类是一个Bean 1
public class userControler {
@RequestMapping("/save") //设置当前操作的访问路径 2
@ResponseBody //设置当前操作的返回值类型3
public String save(){
System.out.println("user save...");
return "{'info':'springmvc'}";
}
}
第三步:创建SpringMVC的配置类
@Configuration
@ComponentScan("com.protectark.mysqlt")
public class SpringMvcConfig {
}
第四步:定义一个servlet容器启动的配置类,在里面加载spring的配置
public class servletInConfig extends AbstractDispatcherServletInitializer {
//加载SpringMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext act = new AnnotationConfigWebApplicationContext();
//注册配置
act.register(SpringMvcConfig.class);
return act;
}
//设置那些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
//设置所有请求归SpringMVC管理
return new String[]{"/"};
}
//加载spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
注解说明
在以上案例中,出现了一些新的注解
注解 | 类型 | 位置 | 作用 |
---|---|---|---|
@Controller | 类注解 | SpringMVC控制器类上 | 设定SpringMvc的核心控制器bean |
@RequestMapping(地址) | 方法注解 | SpringMVC控制器类中方法上 | 设置当前控制器方法请求访问路径 |
@ResponseBody | 方法注解 | SpringMVC控制器方法定义上方 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
工作流程说明
启动服务器初始化过程
- 服务器启动,执行ServletContainersInitConfig类,初始化web容器
- 执行createServletApplicationContext方法,创建了webApplicationcontext对象
- 加载SpringMvcConfig
- 执行@ComponentScan加载对应的bean
- 加载UserController,每个@RequestMapping的名称对应一个具体的方法
- 执行getServletMappings方法,定义所有的请求都通过springMVC
单次请求过程
- 发送请求localhost/ save
- web容器发现所有请求都经过springMVC,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save( )
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方
简化配置类
Spring提供了一个AbstractDispatcherServletInitializer
的子类:
AbstractAnnotationConfigDispatcherServletInitializer
public class servletInConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//Spring配置路径
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{springConfig.class};
}
//SpringMVC配置路径
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{springMvcConfig.class};
}
//拦截路径
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
请求与响应
解决路径冲突
在团队多人开发,如何解决请求路径冲突
设置模块名作为请求路径的前缀
两种方法:
- 修改每个方法的路径
@RuquestMapping
如:
"/user/save"
"/book/save"
- 修改类的请求路径
“/user”
“/save”
“/book”
“/save”
注解 | 类型 | 位置 | 作用 |
---|---|---|---|
@RequestMapping(value) | 方法注解 类注解 | SpringMVC控制器类中方法上 | 设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀 |
示例:
@Controller
@RequestMapping("/user")
public class userControler {
@RequestMapping("/save")
@ResponseBody
public String save(){
return "{'info':'springmvc'}";
}
}
- value(默认):请求访问你路径,或访问路径前缀
解决中文乱码
关于发送的中文乱码,可以在“servlet容器启动的配置类” servletInConfig
中覆盖父类的方法:
为web容器添加过滤器并指定字符集,Spring-web包中提供了专用字符过滤器
//乱码处理
@Override
protected Filter[] getServletFilters() {
//获取字符过滤器
CharacterEncodingFilter filter = new CharacterEncodingFilter();
//设定字符集
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
请求参数与形参不同
例如,定义的方法中形参名为username
和age
,但是实际传参为name
,age
@Controller
public class userControler {
@RequestMapping("/save")
@ResponseBody
public String save(String username, int age){
System.out.println(username + age);
return "{'info':'springmvc'}";
}
}
测试之后就会发现
username为null
此时我们可以使用一个注解,用来声明参数名: @RequestParam("name")
@Controller
public class userControler {
@RequestMapping("/save")
@ResponseBody
public String save(@RequestParam("name") String username,
@RequestParam("age") int age){
System.out.println(username + age);
return "{'info':'springmvc'}";
}
}
传递参数为实体类
如果我们需要的参数过多,比如在新增用户时需要大量的数据,这样我们一般会制作一个实体类,来进行接收。
@Controller
public class userControler {
@RequestMapping("/save")
@ResponseBody
public String save(User user){
return "{'info':'springmvc'}";
}
}
public class User{
private String username;
private int age;
//等其他属性
//重写getter,setter,toString方法
}
这样请求时,如果请求的属性名与实体类中的属性名完全一致,则会自动放入user中。
传递JSON数据
传递JSON数据的时候,我们的数据并不是在请求头的参数中,而是在请求体中。
所以我们在接收的时候也要添加注解,来声明这是请求体中的数据
下面就来实际写一下:
首先应该导入JSON数据转化相关坐标
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
然后告诉SpringMVC开启JSON转化数据
只需要再SpringMvcConfig
中加入@EnableWebMvc
注释,开启JSON转化数据
@Configuration
@ComponentScan("com.protectark.mysqlt")
@EnableWebMvc
public class SpringMvcConfig {
}
由于JSON请求再请求体当中所以,再Controller
方法中
对于形参的修饰不能使用@RequestParam
要使用@RequestBody
@RequestMapping("/inputuser")
@ResponseBody
public String init(@RequestBody User user){
System.out.println("input user ==>"+user);
return "{'info':'springMVC'}";
}
到此完成。
注解 | 类型 | 位置 | 作用 |
---|---|---|---|
@EnableWebMvc |
配置类注解 | SpringMVC配置类定义上方 | 开启SpringMVC多项辅助功能 |
@RequestBody |
形参注解 | Controller方法形参前 | 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次 |
RequestBody
与RequestParam
对比
区别
- @RequestParam用于接收ur1地址传参,表单传参【application/x-www-form-urlencoded)
- @RequestBody用于接收json数据【application/json】
应用
后期开发中,发送json格式数据为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
后面还有一个@PathVariable
属于形参注解,绑定路径参数与形参的关系
@RequestMapping("/users/{id}")
@ResponseBody
public String init(@PathVariable Integer id){
System.out.println("input id ==>"+id);
return "{'info':'springMVC'}";
}
日期类型参数传递
SpringMVC可以直接接收String类型的参数,并自动转为日期类型,如果需要指定格式可以使用注解
@DateTimeFormat
@RequestMapping("/inputuser")
@ResponseBody
public String init(@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date date){
System.out.println("input date ==>"+date);
return "{'info':'springMVC'}";
}
注解 | 类型 | 位置 | 作用 |
---|---|---|---|
@DateTimeFormat |
形参注解 | SpringMVC控制器方法形参前 | 设定日期时间型数据格式 |
属性:pattern:日期时间格式字符串
响应
如果要响应json数据,则只需要返回实体类即可
@RequestMapping("/select")
@ResponseBody
public User select(){
User user = new User();
return user;
}
SpringMVC会自动将实体类转化为json数据
注解 | 类型 | 位置 | 作用 |
---|---|---|---|
@ResponseBody |
方法注解 | SpringMVC控制器方法上 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
REST 风格
概念
REST(Representational state Transfer),表现形式状态转换
- 传统风格资源描述形式
- http://localhost/user/getById?id=1
- http://localhost/user/saveUser
- REST风格描述形式
- http://localhost/user/1
- http://localhost/user
优点:书写简化,隐藏资源访问方式,无法通过地址得知对资源是哪种请求方式。
按照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
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts…
RESTful 快速开发
由于REST风格都使用JSON进行通信,所以每一个方法都需要带有@ResponseBody
,所以我们可以将这个注解放到控制器类上,又因为我们所有的控制器类都有@Controller
和@ResponseBody
注解,所以SpringMVC就将这两个合二为一:@RestController
又因为我们使用的请求方式,绝大多数就四种,所以@RequestMapping
注解也可以根据请求方式衍生出
- @PostMapping
- @GetMapping
- @PutMapping
- @DeleteMapping
使用这种方式,我们的Controller就由
@Controller
public class userControler {
@RequestMapping(value = "/users/save",method = RequestMethod.Get)
@ResponseBody
public String save(@Requestbody User user){
return "{'info':'springmvc'}";
}
@RequestMapping(value = "/users/delete",method = RequestMethod.Delete)
@ResponseBody
public String save(@Requestbody User user){
return "{'info':'springmvc'}";
}
}
变为了
@RestController
@RequestMapping("/users")
public class userControler {
@GetMapping()
public String save(@Requestbody User user){
return "{'info':'springmvc'}";
}
@DeleteMapping()
public String save(@Requestbody User user){
return "{'info':'springmvc'}";
}
}