SpringMVC
学习目标:
1.SpringMVC简介
1)web访问流程

- 1.web服务器通过浏览器访问页面
- 2.前端页面使用异步提交的方式发送请求到后端服务器
- 3.后端服务器采用:表现层---业务层---数据层的架构进行开发
- 4.页面请求由表现层进行接收,获取用户的请求后将参数传递到业务层,再由业务层访问数据层获取数据库中的数据,返回给表现层
- 5.得到用户需要访问的数据之后,表现层通过JSON的格式发送数据到前端页面
- 6.前端页面得到数据之后,解析数据并组织对应的页面渲染到浏览器展示给用户

2)SpringMVC概述
- 是一种基于Java实现的MVC模型的轻量级web框架(表现层框架技术,主要用于表现层开发)
- 优点:
- 使用简单,开发便捷
- 灵活性强
2.SpringMVC入门案例
1)案例实施
- 1.导入
SpringMVC
和Servlet
的坐标
xml
<dependencies>
<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>
</dependencies>
配置tomcat
插件:
xml
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
- 2.创建SpringMVC控制器类:
java
@Controller
public class controller {
@RequestMapping("/print")
@ResponseBody
public String print() {
System.out.println("user print ...");
return "{user: name}";
}
}
- 3.初始化SpringMVC环境,设定SpringMVC加载对应的bean:
SpringMVCConfig
:
java
@Configuration
@ComponentScan("com.springmvclearn.controller")
public class SpringMvcConfig {
}
- 4.初始化Servelet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求:
SpringMvcContainersInitConfig
:
java
public class ServeletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载SpringMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringMvcConfig.class);
return context;
}
//设置哪些请求归属springMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}

涉及的注解介绍
2)入门案例工作流程

3)不同层次结构Bean的加载控制
- 在开发中,Spring一般加载业务层和数据层的bean,SpringMVC一般加载表现层的bean,而入门案例中配置时,Spring加载时会直接扫描整个包下的bean,包括表现层的bean
- 为了避免Spring错误的加载到SpringMVC的bean:我们需要在Spring加载bean时,排除掉SpringMVC的bean
- 1.方式一:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean(SpringBoot中会使用这种方式进行bean的细粒度控制)
java
@Configuration
@ComponentScan(value = "com.springmvclearn",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
- 2.方式二:Spring加载的bean设定扫描范围为精确的包:比如直接对应
service包
,dao包
等。
java
@Configuration
@ComponentScan({"com.springmvclearn.service", "com.springmvclearn.dao"})
public class SpringConfig {
}

4)简化开发的书写格式

5)PostMan
-
网页调试与发送网页http请求的Chrome插件
-
常用于进行接口测试
-
在workspaces中可以云备份历史请求操作
-
创建请求:
-
保持请求,并放入到一个集合中,方便分类复用:
3.请求与响应
1.请求映射路径
-
当相同层级下的controller有相同的路径时(
/save
),在访问时就会有路径冲突问题 -
一般解决就是在不同controller的路径前加入不同模块的请求路径前缀:
/user/save
、/book/save
-
通过
RequestMapping
在控制类上统一设置当前控制类下的方法请求路径前缀,然后在每个控制器方法上再加每个控制方法对应的请求访问路径
2.请求参数
1)普通参数请求
java
//请求参数
@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'}";
}
//普通参数:请求参数名与形参名不同时,使用@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'}";
}
}
- 1.GET 请求:
- 2.POST请求:
- 3.POST中文参数乱码问题
java
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}

2)POJO参数传递
- 当大量参数需要传递时,一般将参数封装成一个实体类,在参数传递时,直接将参数赋值给实体类对象,然后从实体类对象中拿取对应参数值
User
:
java
package com.itheima.domain;
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;
}
}
在controller中:
java
//请求参数
@Controller
public class UserController {
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
System.out.println("userName ==> "+user.getName());
System.out.println("userAge ==> "+user.getAge());
return "{'module':'pojo param'}";
}
}
3)嵌套POJO参数传递
- 当POJO中还有引用的POJO时,如何传递参数
- 也就是在POJO中将该POJO注入,并给出setter和getter方法:
user
:
java
package com.itheima.domain;
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;
}
}
然后在controller中同样接收POJO对象即可:
java
//请求参数
@Controller
public class UserController {
//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
System.out.println("userName ==> "+user.getName());
System.out.println("userAge ==> "+user.getAge());
System.out.println("userAddress ==> "+user.getAddress());
return "{'module':'pojo contain pojo param'}";
}
}


4)数组参数传递
- 需要传入一个数组类型的参数时:
java
//请求参数
@Controller
public class UserController {
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] hobby){
System.out.println("数组参数传递 hobby ==> "+ Arrays.toString(hobby));
return "{'module':'array param'}";
}
}


5)集合类型参数
- 需要集合类型参数时:我们需要设置该集合参数为
RequestParam
,否则无法识别需要传递的是集合
java
//请求参数
@Controller
public class UserController {
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'module':'list param'}";
}
}
请求则同上,结果也是一样的。

3.JSON格式参数传递
- 首先要加入解析JSON的坐标:
xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
1)JSON格式的集合参数
- 1.开启支持集合JSON格式的数据类型自动转换:
java
@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
- 2.加上在参数前面加入
@RequestBody
,表示传入的参数是在JSON的body中
java
//请求参数
@Controller
public class UserController {
//集合参数: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'}";
}
}
2)JSON格式的POJO参数传递(以及嵌套POJO)
- 接收参数格式:
java
//请求参数
@Controller
public class UserController {
//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'}";
}
}
-
参数请求格式:
-
结果:
3)JSON格式对象集合参数传递
- 接收参数格式:
java
//请求参数
@Controller
public class UserController {
//集合参数: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'}";
}
}
- 参数请求格式:

- 结果:
4.对比

5.日期型参数传递
- 接收参数格式:
java
//请求参数
@Controller
public class UserController {
//日期参数
//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
System.out.println("参数传递 date ==> "+date);
System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
return "{'module':'data param'}";
}
}
- 参数请求格式:

- 结果:
6.响应
- 1.响应页面
- 2.响应文本
- 3.响应POJO
- 4.响应POJO集合
使用@ResponseBody
- 将其注解在SpringMVC控制器方法定义的上方
- 作用:设置当前控制器返回值为响应体(POJO转JSON,POJO集合转JSON)
java
//请求参数
@Controller
public class UserController {
//返回页面
@RequestMapping("/pageParam")
public String pageParam() {
System.out.println("返回页面");
return "index.jsp";
}
//返回文本数据
@RequestMapping("/textParam")
@ResponseBody
public String totext() {
System.out.println("返回文本数据");
return "context text";
}
//返回POJO
@RequestMapping("/userParam")
@ResponseBody
public User userParam(User user) {
System.out.println("返回POJO");
User new_user = new User();
new_user.setName(user.getName());
new_user.setAge(user.getAge());
new_user.setAddress(user.getAddress());
return new_user;
}
//返回POJO集合
@RequestMapping("/usersParam")
@ResponseBody
public List<User> usersParam(List<User> user) {
System.out.println("返回POJO集合");
List<User> new_users = new ArrayList<>();
new_users.addAll(user);
return new_users;
}
}
4.REST风格
-
是一种定义资源描述的形式
-
REST风格访问资源时,使用
行为动作
区分对资源进行了何种操作:路径+请求方式
1)RESTful
-
在请求时通过不同的请求方式
-
@PathVariable
接收路径参数或者路径变量(路径中的参数获取:http://localhost/users/3
-> 3的获取)
java
package com.itheima.controller;
import com.itheima.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@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'}";
}
}
2)@RequestBody
、@RequestParam
、@PathVariable

3)RESTful快速开发
-
在1)RESTful的编写中,同一个路径前缀等在每个控制器方法上都有,为了简化书写,以下注解和简化写法是标准的RESTful开发:
-
标准RESTful开发:
java
package com.itheima.controller;
import com.itheima.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
//@Controller
//@ResponseBody配置在类上可以简化配置,表示设置当前每个方法的返回值都作为响应体
//@ResponseBody
@RestController //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RequestMapping("/books")
public class BookController {
// @RequestMapping( method = RequestMethod.POST)
@PostMapping //使用@PostMapping简化Post请求方法对应的映射配置
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
// @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
@DeleteMapping("/{id}") //使用@DeleteMapping简化DELETE请求方法对应的映射配置
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping //使用@PutMapping简化Put请求方法对应的映射配置
public String update(@RequestBody Book book){
System.out.println("book update..."+book);
return "{'module':'book update'}";
}
// @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
@GetMapping("/{id}") //使用@GetMapping简化GET请求方法对应的映射配置
public String getById(@PathVariable Integer id){
System.out.println("book getById..."+id);
return "{'module':'book getById'}";
}
// @RequestMapping(method = RequestMethod.GET)
@GetMapping //使用@GetMapping简化GET请求方法对应的映射配置
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
4)案例:基于RESRful页面数据交互
-
1.制作控制器,通过PostMan测试接口:
-
2.放行静态资源访问:
由于SpringMVC设置了访问路径拦截,当访问前端静态页面时,会被拦截,导致无法访问到,于是我们需要制作放行器,将对应的静态数据进行放行:
SpringMVCSupport
:
java
package com.itheima.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SpringSupport extends WebMvcConfigurationSupport {
//当访问/pages/????的时候,走/pages目录下的内容
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
- 3.异步提交访问后端控制器获取数据:
5.拦截器
- 动态拦截方法调用的机制,在SpringMVC中动态拦截控制器的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行(权限控制)
1)拦截器与过滤器区别
- 归属不同:Filter属于Serverlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter针对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
2)拦截器入门案例
- 1.声明拦截器的bean,并实现HandlerInterceptor接口:
java
@Component
public class MyProjectInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyProjectInterceptor posHandle");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyProjectInterceptor preHandle");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyProjectInterceptor afterCompletion");
}
}
- 2.定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法:
java
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
}
- 3.添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个:
java
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {
@Autowired
private MyProjectInterceptor projectInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/page/**").addResourceLocations("classpath:/pages/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截器配置,当遇到哪些路径时,会将其拦截下来执行对应的postHandle和preHandle方法
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
}
}
- 4.发送请求:
- 5.拦截器拦截对应的路径下方法的调用,并在方法调用前后执行预先定义的方法:
拦截器执行流程:

3)拦截器参数(了解即可,主要用到的可能是前置处理)
- 前置处理,拿到handler(原始方法对象的封装)可以对原始方法进行一些处理
- 主要用处为在方法执行前作校验
4)拦截器链(了解即可)
- 当配置多个拦截器是,形参拦截器链
- 拦截器链的运行顺序参照拦截器添加的顺序为准
*注:上述内容均来自黑马程序员SSM框架的课程学习,仅用作学习交流,不作为商业用途,如有侵权,联系删除。