一、SpringMVC简介
1. SpringMVC概述
- SpringMVC技术与Servlet技术功能等同,均属于web层(表现层)开发技术
- SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
- 优点:
- 使用简单,开发便捷(相比于Servlet)
- 灵活性强
二、入门案例
使用SpringMVC需要先导入SpringMVC坐标与Servlet坐标
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>6.0.11</version> </dependency>
创建SpringMVC控制器类(等同于Servlet功能)
@Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save() { System.out.println("UserController.save()"); return "{'info':'SpringMVC'};"; }; };
初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
@Configuration @ComponentScan("com.cikian") public class SpringMvcConfig { };
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return null; }; @Override protected String[] getServletMappings() { return new String[]{"/"};; }; @Override protected WebApplicationContext createRootApplicationContext() { return null; }; };
SpringMVC入门程序开发总结(1+N):
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
- 多次工作
- 定义处理请求的控制器类
- 定义处理请求的控制器方法,并配置映射路径(
@RequestMapping
)与返回json数据(@ResponseBody
)
- 一次性工作
三、工作流程分析
- 启动服务器初始化过程
- 服务器启动,执行ServletContainersInitConfig类,初始化web容器
- 执行createServletApplicationContext方法,创建了WebApplicationContext对象
- 加载SpringMvcConfig
- 执行
@ComponentScan
加载对应的bean - 加载JserController,每个
@RequestMapping
的名称对应一个具体的方法 - 执行getServletMappings,方法,定义所有的请求都通过SpringMVC
- 单次请求过程
- 发送请求localhost/save
- web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 执行save()
- 检测到有
@ResponseBody
直接将save()方法的返回值作为响应求体返回给请求方
四、bean加载控制
Controller加载控制与业务bean加载控制
- SpringMVC相关bean(表现层bean)
- Spring控制的bean
- 业务bean(Service)
- 功能bean(DataSources等)
如何避免Spring加载SpringMVC加载的bean
SpringMVC相关bean加载控制
- SpringMVC加载的bean对应的包均在com.controller包内
Spring相关bean加载控制
方式一:Spring加载的bean设定扫描范围为com,排除掉controller包内的bean,如:
@ComponentScan(value = "com.cikian", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) )
方式二:Spring加载的bean设定扫描范围为精准范围,例如servicet包、dao包等,如:
@ComponentScan({"com.cikian.service", "com.cikian.dao"})
方式三:不区分Spring与SpringMVCl的环境,加载到同一个环境中
名称:
@ComponentScan
类型:类注解
范例:
@Configuration @ComponentScan(value = "com.cikian", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { };
属性:
- excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classe)
- includeFilters:加载指定的bean,需要指定类别(type)与具体项(classe)
简化
ServletContainersInitConfig
配置类: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[]{"/"};; }; };
五、请求
1. 请求映射路径
为了防止请求路径设置重复,可以给不同模块设置不同的请求路径前缀
名称:
@RequestMapping
类型:方法注解 类注解
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀
范例:
@RequestMapping("/user") @Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save() { System.out.println("user/save执行。。。"); return "{'info':'usersave'};"; }; @RequestMapping("/data") @ResponseBody public String data() { System.out.println("user/data执行。。。"); return "{'msg':'userdata'};"; }; };
属性:
value(默认):请求访问路径,或访问路径前缀
2. 接收请求参数
接收请求参数:直接在方法中加上对应的形参即可:
@RequestMapping("/commonParam") @ResponseBody public String commonParam(String name,int age) { System.out.println("参数传递:" + name); System.out.println("参数传递:" + age); return "{'msg':'commonParam'};"; };
处理中文乱码问题
在ServletContainersInitConfig配置类中,设置过滤器,重写getServletFilters方法:
@Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF8"); return new Filter[]{characterEncodingFilter};; };
3. 参数种类
普通参数
url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。或使用@RequestParam
注解将请求参数与形参绑定,如:
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(@RequestParam("username") String name, int age) {
System.out.println("参数传递:" + name);
System.out.println("参数传递:" + age);
return "{'msg':'commonParam'};";
};
POJO类型参数
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user) {
System.out.println("参数传递:" + user);
return "{'msg':'pojoParam'};";
};
User类中有name与age属性,参数传递过来时,会自动将对应名称的属性封装进pojo类
嵌套POJO类型参数
当pojo类型参数的实体对象中有引用的对象时,如User类中还引用Address类,此时可以下面方式传递:
127.0.0.1/user/pojoParam2/?name=张三&age=18&address.province=河北&address.city=秦皇岛
数组类型参数
@RequestMapping("/arrayParam")
@ResponseBody
public String pojoParam(String[] likes) {
System.out.println("参数传递:" + Arrays.toString(likes));
return "{'msg':'arrayParam'};";
};
传递数组类型参数时,在访问时,使用多个向明名称的访问参数,如:
localhost/user/arrayParam?likes=吃&likes=喝&likes=拉&likes=撒&likes=睡
集合类型参数
当接收List集合参数时,传递参数的方式与数组类型参数传递方式一样,在接收参数时,要在形参前使用@RequestParam
注解,如:
@RequestMapping("/listParam")
@ResponseBody
public String pojoParam(@RequestParam List<String> likes) {
System.out.println("参数传递:" + likes);
return "{'msg':'listParam'};";
};
传递json数据
导入处理json数据的坐标
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
在SpringMvcConfig中使用
@EnableWebMvc
注解开启由json数据转换成对象的功能@Configuration @ComponentScan("com.cikian.controller") @EnableWebMvc public class SpringMvcConfig {};
在就收参数的方法的形参前加
@RequestBody
注解传递json数组
@RequestMapping("/jsonParam") @ResponseBody public String jsonParam(@RequestBody List<String> likes) { System.out.println("参数传递:" + likes); return "{'msg':'jsonParam'};"; };
请求参数:
["game","music","code"]
传递json对象(POJO)
@RequestMapping("/jsonPojoParam") @ResponseBody public String jsonPojoParam(@RequestBody User user) { System.out.println("参数传递:" + user); return "{'msg':'jsonPojoParam'};"; };
请求参数:
{ "name":"陈", "age":18, "address":{ "province":"河北", "city":"秦皇岛" }; };
传递json对象数组(POJO)
@RequestMapping("/jsonPojoListParam") @ResponseBody public String jsonPojoListParam(@RequestBody List<User> users) { System.out.println("参数传递:" + users); return "{'msg':'jsonPojoListParam'};"; };
请求参数:
[ { "name":"张三", "age":15, "address":{ "province":"河北", "city":"秦皇岛" }; };, { "name":"李四", "age":18, "address":{ "province":"北京", "city":"北京" }; }; ]
- 名称:
@RequestBody
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
@RequestBody
与@RequestParam
- 区别:
@RequestParam
用于接收url地址传参,表单传参【application/X-www-form-urlencoded】@RequestBody
用于接收json数据【application/json】
- 应用:
- 后期开发中,发送json格式数据为主,
@RequestBody
应用较广 - 如果发送非json格式数据,选用
@RequestParam
接收请求参数
- 后期开发中,发送json格式数据为主,
- 名称:
日期类型参数传递
日期类型数据基于系统不同格式也不尽相同,如:
- 2023-08-07
- 2023/08/07
- 07/08/2023
默认接收"yyyy/MM/dd HH:mm:ss"
格式日期,可以使用@DateTimeFormat
注解自定义传入格式:
@RequestMapping("/dateParam")
@ResponseBody
public String dateParam(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 "{'msg':'dateParam'};";
};
如此定义,请求参数:
localhost/user/dateParam?date1=2023/08/07 20:30:50&date2=2023-08-07&date3=2023-08-07 15:25:30
中的三种日期都能正确接收
- 名称:
@DateTimeFormat
- 类型:形参注解
- 位置:SpringMVC控制器方法形参前面
- 作用:设定日期时间型数据格式
- 属性:parattern:日期时间格式字符串
Converter接口
public interface Converter<S, T> { @Nullable T convert(S var1); };
- 请求参数年龄数据(String→Integer)
- 日期格式转换(String→Date)
- ……
@EnableWebMvc
功能之一:根据类型匹配对应的类型转换器
六、响应
1. 响应页面
@RequestMapping("/toJumpPage")
public String toJumpPage() {
System.out.println("跳转页面。。。");
return "/page.jsp";
};
在方法中返回的字符串默认会被当做响应的页面文件的名称
2. 响应数据
响应文本数据:
@RequestMapping("/toText") @ResponseBody public String toText() { System.out.println("返回纯文本"); return "response text"; };
响应json数据(对象转json):
@RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO() { System.out.println("返回json对象"); User user = new User(); user.setName("陈cikian"); user.setAge(18); return user; };
响应json数组(对象集合转json数组):
@RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList() { User user1 = new User(); user1.setName("张三"); user1.setAge(15); User user2 = new User(); user2.setName("李四"); user2.setAge(120); List<User> users = Arrays.asList(user1, user2); return users; };
- 名称:
@ResponseBody
- 类型:方法注解
- 位置:SpringMVC控制器方法定义上方
- 作用:设置当前控制器方法响应内容为当前返回值,无需解析
类型转换的功能来自HttpMessageConverter接口
七、REST风格
1. 简介
REST(Representational State Transfer),表现形式状态转换
优点:
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
-
查询全部用户信息
使用GET使用代表
http://localhost/users
添加用户信息
使用P0ST提交代表新增/保存http://localhost/users
修改用户信息
使用PUT使用修改/更新http://localhost/users/1
查询指定用户信息
使用GET提交代表查询http://localhost/users/1
删除用户信息
使用DELETE提交代表删除
- 上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
- 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts.…
-
根据REST风格对资源进行访问称为RESTful
2. RESTful入门案例
@Controller
public class UserController {
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(User user) {
System.out.println("user.save:" + user);
return "{'info':'user.save'};";
};
@RequestMapping(value = "/users/{id};", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user.delete:" + id);
return "{'info':'user.delete'};";
};
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user.update:" + user);
return "{'info':'user.update'};";
};
@RequestMapping(value = "/users/{id};", method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id) {
System.out.println("user.getById:" + id);
return "{'info':'user.getById'};";
};
@RequestMapping(value = "/users", method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user.getAll:");
return "{'info':'user.getAll'};";
};
};
设定http请求动作,如:
method = RequestMethod.POST
设定请求参数(路径变量)
请求路径中要有对应参数的占位,如:
/users/{id}
,形参前使用@PathVariable
注解,如:@PathVariable Integer id
,占位符的名称应与形参名称相同。
- 名称:
@RequestMapping
- 类型:方法注解
- 位置:SpringMVC控制器方法定义上方
- 作用:设置当前控制器方法请求访问路径
- 属性:
- value(默认):请求访问路径
- method:http请求动作,标准动作(GET/POST/PUT/DELETE)
- 名称:
@PathVariable
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应
@RequestBody
@RequestParam
@PathVariable
- 区别
@RequestParam
用于接收url地址传参或表单传参@RequestBody
用于接收请求体数据,主要为json数据@PathVariable
用于接收路径参数,使用{参数名称}描述路径参数- 应用
- 后期开发中,发送请求参数超过1个时,以json格式为主,
@RequestBody
应用较广- 如果发送非json格式数据,选用
@RequestParam
接收请求参数- 采用RESTfuli进行开发,当参数数量较少时,例如1个,可以采用
@PathVariable
接收请求路径变量,通常用于传递id值
3. REST快速开发
上述案例代码可修改为以下内容:
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public String save(@RequestBody User user) {
System.out.println("user.save:" + user);
return "{'info':'user.save'};";
};
@DeleteMapping("/{id};")
public String delete(@PathVariable Integer id) {
System.out.println("user.delete:" + id);
return "{'info':'user.delete'};";
};
@PutMapping
public String update(@RequestBody User user) {
System.out.println("user.update:" + user);
return "{'info':'user.update'};";
};
@GetMapping("/{id};")
public String getById(@PathVariable Integer id) {
System.out.println("user.getById:" + id);
return "{'info':'user.getById'};";
};
@GetMapping
public String getAll() {
System.out.println("user.getAll:");
return "{'info':'user.getAll'};";
};
};
- 名称:
@RestController
- 类型:类注解
- 位置:基于SpringMVC的RESTful开发控制器类定义上方
- 作用:设置当前控制器类为RESTful,风格,等同于
@Controller
与@ResponseBody
两个注解组合功能
- 名称:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
- 类型:方法注解
- 位置:基于SpringMVC的RESTful开发控制器方法定义上方
- 作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,如:
@GetMapping
对应GET请求- 属性:value(默认):请求访问路径
八、补充:对静态资源放行
由于设置了SpringMVC拦截所有路径请求,所以会导致访问不到静态资源,如html文件、css文件、js文件、图片等,所以需要设置放行拦截路径,在config包下创建SpringMvcSupport配置类,内容如下:
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("classpath:/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/css/");
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
registry.addResourceHandler("/img/**").addResourceLocations("classpath:/img/");
};
};
然后在SpringMVC配置类中将此配置类导入,导入方法不唯一,例如:@ComponentScan({"com.cikian.controller", "com.cikian.config"})