SpringMVC基础学习
1. SpringMVC简介
- SpringMVC技术与Servlet技术功能等同,均属于web层开发技术
- SpringMVC是一种基于Java实现的MVC模型的轻量级web框架
- 优点:使用简单,开发便捷,灵活性强
-
基本使用:
- 导入SpringMVC坐标与Servlet坐标
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.22.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
- 创建SpringMVC控制器类(等同于Servlet功能)
@COntroller
:SpringMVC控制器类,设定SpringMVC的核心控制器bean@ResponseBody
:设置当前控制器方法响应内容为当前返回值,无需解析@RequestMapping
:设置当前控制器方法请求访问路径
@Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(String str){ System.out.println("UserController save doing ..."); return "{'model:'springmvc'}"; } }
- 初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
@Configuration @ComponentScan({"com.cqut.controller"}) public class SpringMVCConfig { }
- 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
- AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
- createServletApplicationContext()方法,创建Servlet容器时,SpringMVC类型的bean被注册,进而加载由SpringMVCConfig配置中所有的bean,放入WebApplicationContextt对象范围中
- WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围
public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer { protected WebApplicationContext createServletApplicationContext() { //这里需要和Spring中加载配置文件做区分 AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(SpringMVCConfig.class); return context; } protected String[] getServletMappings() { //拦截对应请求进行请求响应 return new String[]{"/"}; } protected WebApplicationContext createRootApplicationContext() { return null; } }
可能出现问题:
spring-webmvc 5.3出现javax.servlet.http.HttpServletResponse.setContentLengthLong(J)V报错
说明:这里导入的spring-webmvc的是5.3版本的抛出异常,需要将版本降低到5.3版本一下,就没有异常了。
注意:tomcat是对war包进行运行,jar包并不能执行。
2. 工作流程
2.1. 服务器启动初始化过程
- 服务器启动,执行ServletContainerInitConfig类,初始化web容器
- 执行createServletApplicationContext方法,创建WebApplicationContext对象
- 加载SpringMVCConfig
- 执行@ComponentScan加载对应的bean
- 加载UserController,每个@RequestMapping的名称对应一个具体的方法
- 执行getServletMapping方法,定义所有请求都通过SpringMVC
2.2 单次请求过程
- 发送请求
- web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
3. Bean加载控制
3.1. 控制范围
- SpringMVC控制bean:controller(表现层)
- SpringMVC加载的bean对应的包均在com.cqut.controller包内
- Spring控制bean:service(业务层)功能(DataSource等)
- 方式一:Spring加载的bean设定扫描范围为com.cqut,排除controller包内的bean
- 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包,dao包等
- 方式三:不区分Spring与SpringMVC的环境,加载到同一环境中
3.2. 代码实现
-
方式一:
@ComponentScan({"com.cqut.controller"}) public class SpringMvcConfig { } @Configuration @ComponentScan(value = "com.cqut", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
- excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体相(classes)
- includeFilters:加载指定bean,需要指定类别(type)与具体相(classes)
- 这种方式下,不能在SpringMvcConfig加上注解@Configuration,否则依旧会扫描到controller
-
方式二:
@Configuration @ComponentScan({"com.cqut.controller"}) public class SpringMvcConfig { } @Configuration @ComponentScan({"com.cqut.dao", "com.cqut.service"}) public class SpringConfig { }
3.3. 简化开发
- 将继承AbstractDispatcherServletInitializer这个类替换为其子类
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
4. 请求与响应
4.1. 请求映射路径
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(String str){
System.out.println("UserController save doing ...");
return "{'model:'springmvc'}";
}
}
@RequestMapping
:- 类型:方法注解,类注解
- 设置当前控制器方法请求访问路径,如果设置在类上同一设置当前控制器方法请求访问路径前缀
4.2. 请求参数
- Get方法传参接收
- url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
@RequestMapping("/save0")
@ResponseBody
public String save0(String username, String password, Integer age){
System.out.println("Username = " + username);
System.out.println("Password = " + password);
System.out.println("Age = " + age);
return "Get:{'model:'springmvc'}";
}
- Post请求传参接收
- form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数
@RequestMapping("/save1")
@ResponseBody
public String save1(String username, String password, Integer age){
System.out.println("Username = " + username);
System.out.println("Password = " + password);
System.out.println("Age = " + age);
return "Post:{'model:'springmvc'}";
}
- 处理Post请求参数中文乱码
- 为web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器
- 可以设置多个过滤器
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter1 = new CharacterEncodingFilter();
filter1.setEncoding("utf-8");
return new Filter[]{filter1};
}
-
五种参数传参处理
- 普通参数:url地址传参,地址参数名和形参变量名相同,定义形参即可接收参数
@RequestParam
:形参注解,绑定请求参数与处理器方法形参间的关系。当请求中参数名与处理器方法形参名称不一致的,进行绑定
@RequestMapping("/save2") @ResponseBody public String save2(@RequestParam("username") String name, String password, Integer age){ System.out.println("Username = " + name); System.out.println("Password = " + password); System.out.println("Age = " + age); return "Post:{'model:'springmvc'}"; }
- POJO类型参数:请求参数名与新参对象属性名相同,定义POJO形参即可接收参数
@RequestMapping("/save3") @ResponseBody public String save3(User user){ System.out.println(user); return "Post:{'model:'springmvc'}"; }
- 普通参数:url地址传参,地址参数名和形参变量名相同,定义形参即可接收参数
-
嵌套POJO类型参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
@RequestMapping("/save6") @ResponseBody public String save6(Account account){ System.out.println(account); return "Post:{'model:'springmvc'}"; }
@Data public class Account { private String num; private User user; }
-
数组类型参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数
@RequestMapping("/save4") @ResponseBody public String save4(String[] courses){ System.out.println(Arrays.toString(courses)); return "Post:{'model:'springmvc'}"; }
-
集合类型参数:请求参数与形参对象名相同且相同且请求参数为多个,
@RequestParam
绑定参数关系@RequestMapping("/save5") @ResponseBody public String save5(@RequestParam List<String> courses){ System.out.println(courses); return "Post:{'model:'springmvc'}"; }
4.2.1 json传递参数(重点 常用)
@RequestBody
与@RequestParam
区别:
@RequestParam
用于接收url地址传参,表单传参[application/x-www-form-urlencoded]@RequestBody
用于接收json数据[application/json]- 后期开发中,发送json格式数据为主,
@RequestBody
应用较广,如果发送非json格式数据,选用@RequestParam
接收请求参数
操作演示:
- 处理Json格式依赖坐标如下:(提供数据转换的功能)
- 这里需要注意jdk和导入坐标版本兼容性问题
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
- 开启数据转换的功能
@Configuration
@ComponentScan({"com.cqut.controller"})
@EnableWebMvc
public class SpringMvcConfig {
}
- 普通数组传参
@RequestMapping("/save0")
@ResponseBody
public String save0(@RequestBody List<String> courses){
System.out.println(courses);
return "{'model':'array for json param'}";
}
- 对象(嵌套对象)传参
@RequestMapping("/save1")
@ResponseBody
public String save1(@RequestBody Account account){
System.out.println(account);
return "{'model':'pojo for json param'}";
}
- 集合对象传参
@RequestMapping("/save2")
@ResponseBody
public String save2(@RequestBody List<User> users){
System.out.println(users);
return "{'model':'list for json param'}";
}
4.3. 日期类型参数传递
- 日期类型数据基于系统不同格式也不同,在接受形参时,根据不同的日期格式设置不同的接收方式
@RequestMapping("/save3")
@ResponseBody
public String save3(Date date1,
@DateTimeFormat(pattern = "yyyy-MM-dd") Date date2,
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date date3){
System.out.println(date1);
System.out.println(date2);
System.out.println(date3);
return "{'model':'data param show'}";
}
@DataTimeFormat
:设定日期时间数据格式。Pattern属性:日期时间格式字符串Converter
接口(类型转换器):有多个实现类来实现各种数据的格式转换
package org.springframework.core.convert.converter;
import org.springframework.lang.Nullable;
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S var1);
}
4.4. 响应json数据
- 响应页面
- 响应数据
- 文本数据
- json数据
@RequestMapping("/index")
@ResponseBody
public List<User> save7(){
List<User> users = new ArrayList<User>();
User user1 = new User();
user1.setPassword("123");
user1.setUsername("xln");
user1.setAge(12);
users.add(user1);
User user2 = new User();
user2.setPassword("1313");
user2.setUsername("xln");
user2.setAge(20);
users.add(user2);
return users;
}
- HttpMessageConverter接口(将数据转换成json格式)
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> var1, @Nullable MediaType var2);
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}
4.5. REST风格
- REST(Representational State Transfer),表现形式状态转换
- 优点:隐藏资源的访问行为,无法通过地址得知对资源是何种操作
请求方式 | 操作 | REST快速开发 |
---|---|---|
GET | 查询 | @GetMapping |
POST | 新增/保存 | @PostMapping |
PUT | 修改/更新 | @PutMapping |
DELETE | 删除 | @DeleteMapping |
参数注解 | 来源 |
---|---|
@RequestParam | 用于接收url地址传参或表单传参 |
@RequestBody | 用于接收json数据 |
@PathVariable | 用于接收路径参数,使用{参数名称}描述路径参数 |
@RestController
:设置当前控制器类为RESTful风格,等同于@Controller
与@ResponseBody
两个注解组合功能
4.6. 放行静态资源访问
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
}
}
- 当访问/xx/**,走/xx/目录下的内容
- 注意这里是从webapp目录下走的,文件夹命名一定要是webapp
4.6. 放行静态资源访问
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
}
}
- 当访问/xx/**,走/xx/目录下的内容
- 注意这里是从webapp目录下走的,文件夹命名一定要是webapp
- 同时SpringMvcSupport这个配置类一定要是SpringMvc这个类通过包扫描进行加载(试过其他各种加载都没加载上,原因未知)