【SpringMVC】SSM框架【二】——SpringMVC超详细
SpringMVC
学习目标:
1.SpringMVC简介
1)web访问流程
- 1.web服务器通过浏览器访问页面
- 2.前端页面使用异步提交的方式发送请求到后端服务器
- 3.后端服务器采用:表现层—业务层—数据层的架构进行开发
- 4.页面请求由表现层进行接收,获取用户的请求后将参数传递到业务层,再由业务层访问数据层获取数据库中的数据,返回给表现层
- 5.得到用户需要访问的数据之后,表现层通过JSON的格式发送数据到前端页面
- 6.前端页面得到数据之后,解析数据并组织对应的页面渲染到浏览器展示给用户
2)SpringMVC概述
- 是一种基于Java实现的MVC模型的轻量级web框架(表现层框架技术,主要用于表现层开发)
- 优点:
- 使用简单,开发便捷
- 灵活性强
2.SpringMVC入门案例
1)案例实施
- 1.导入
SpringMVC
和Servlet
的坐标
<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
插件:
<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控制器类:
@Controller
public class controller {@RequestMapping("/print")@ResponseBodypublic String print() {System.out.println("user print ...");return "{user: name}";}
}
- 3.初始化SpringMVC环境,设定SpringMVC加载对应的bean:
SpringMVCConfig
:
@Configuration
@ComponentScan("com.springmvclearn.controller")
public class SpringMvcConfig {
}
- 4.初始化Servelet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求:
SpringMvcContainersInitConfig
:
public class ServeletContainersInitConfig extends AbstractDispatcherServletInitializer {//加载SpringMVC容器配置@Overrideprotected WebApplicationContext createServletApplicationContext() {AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();context.register(SpringMvcConfig.class);return context;}//设置哪些请求归属springMVC处理@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}@Overrideprotected 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的细粒度控制)
@Configuration
@ComponentScan(value = "com.springmvclearn", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
)
public class SpringConfig {
}
- 2.方式二:Spring加载的bean设定扫描范围为精确的包:比如直接对应
service包
,dao包
等。
@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)普通参数请求
//请求参数
@Controller
public class UserController {//普通参数:请求参数与形参名称对应即可完成参数传递@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name ,int age){System.out.println("普通参数传递 name ==> "+name);System.out.println("普通参数传递 age ==> "+age);return "{'module':'common param'}";}//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系@RequestMapping("/commonParamDifferentName")@ResponseBodypublic 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中文参数乱码问题
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[]{"/"};}//乱码处理@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");return new Filter[]{filter};}
}
2)POJO参数传递
- 当大量参数需要传递时,一般将参数封装成一个实体类,在参数传递时,直接将参数赋值给实体类对象,然后从实体类对象中拿取对应参数值
User
:
package com.itheima.domain;public class User {private String name;private int age;@Overridepublic 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中:
//请求参数
@Controller
public class UserController {//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递@RequestMapping("/pojoParam")@ResponseBodypublic 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
:
package com.itheima.domain;public class User {private String name;private int age;private Address address;@Overridepublic 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对象即可:
//请求参数
@Controller
public class UserController {//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递@RequestMapping("/pojoContainPojoParam")@ResponseBodypublic 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)数组参数传递
- 需要传入一个数组类型的参数时:
//请求参数
@Controller
public class UserController {//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中@RequestMapping("/arrayParam")@ResponseBodypublic String arrayParam(String[] hobby){System.out.println("数组参数传递 hobby ==> "+ Arrays.toString(hobby));return "{'module':'array param'}";}
}
5)集合类型参数
- 需要集合类型参数时:我们需要设置该集合参数为
RequestParam
,否则无法识别需要传递的是集合
//请求参数
@Controller
public class UserController {//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据@RequestMapping("/listParam")@ResponseBodypublic String listParam(@RequestParam List<String> likes){System.out.println("集合参数传递 likes ==> "+ likes);return "{'module':'list param'}";}
}
请求则同上,结果也是一样的。
3.JSON格式参数传递
- 首先要加入解析JSON的坐标:
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency>
1)JSON格式的集合参数
- 1.开启支持集合JSON格式的数据类型自动转换:
@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
- 2.加上在参数前面加入
@RequestBody
,表示传入的参数是在JSON的body中
//请求参数
@Controller
public class UserController {//集合参数:json格式//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据@RequestMapping("/listParamForJson")@ResponseBodypublic 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)
- 接收参数格式:
//请求参数
@Controller
public class UserController {//POJO参数:json格式//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc//2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应@RequestMapping("/pojoParamForJson")@ResponseBodypublic String pojoParamForJson(@RequestBody User user){System.out.println("pojo(json)参数传递 user ==> "+user);return "{'module':'pojo for json param'}";}
}
-
参数请求格式:
-
结果:
3)JSON格式对象集合参数传递
- 接收参数格式:
//请求参数
@Controller
public class UserController {//集合参数:json格式//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应@RequestMapping("/listPojoParamForJson")@ResponseBodypublic String listPojoParamForJson(@RequestBody List<User> list){System.out.println("list pojo(json)参数传递 list ==> "+list);return "{'module':'list pojo for json param'}";}
}
- 参数请求格式:
- 结果:
4.对比
5.日期型参数传递
- 接收参数格式:
//请求参数
@Controller
public class UserController {//日期参数//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd@RequestMapping("/dataParam")@ResponseBodypublic 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)
//请求参数
@Controller
public class UserController {//返回页面@RequestMapping("/pageParam")public String pageParam() {System.out.println("返回页面");return "index.jsp";}//返回文本数据@RequestMapping("/textParam")@ResponseBodypublic String totext() {System.out.println("返回文本数据");return "context text";}//返回POJO@RequestMapping("/userParam")@ResponseBodypublic 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")@ResponseBodypublic 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的获取)
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)@ResponseBodypublic String save(){System.out.println("user save...");return "{'module':'user save'}";}//设置当前请求方法为DELETE,表示REST风格中的删除操作//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)@ResponseBodypublic String delete(@PathVariable Integer id){System.out.println("user delete..." + id);return "{'module':'user delete'}";}//设置当前请求方法为PUT,表示REST风格中的修改操作@RequestMapping(value = "/users",method = RequestMethod.PUT)@ResponseBodypublic 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)@ResponseBodypublic String getById(@PathVariable Integer id){System.out.println("user getById..."+id);return "{'module':'user getById'}";}//设置当前请求方法为GET,表示REST风格中的查询操作@RequestMapping(value = "/users",method = RequestMethod.GET)@ResponseBodypublic String getAll(){System.out.println("user getAll...");return "{'module':'user getAll'}";}
}
2)@RequestBody
、@RequestParam
、@PathVariable
3)RESTful快速开发
-
在1)RESTful的编写中,同一个路径前缀等在每个控制器方法上都有,为了简化书写,以下注解和简化写法是标准的RESTful开发:
-
标准RESTful开发:
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
:
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目录下的内容@Overrideprotected 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接口:
@Component
public class MyProjectInterceptor implements HandlerInterceptor {@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("MyProjectInterceptor posHandle");}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("MyProjectInterceptor preHandle");return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("MyProjectInterceptor afterCompletion");}
}
- 2.定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法:
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {@Overridepublic void addInterceptors(InterceptorRegistry registry) {}
}
- 3.添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个:
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {@Autowiredprivate MyProjectInterceptor projectInterceptor;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/page/**").addResourceLocations("classpath:/pages/");}@Overridepublic void addInterceptors(InterceptorRegistry registry) {//拦截器配置,当遇到哪些路径时,会将其拦截下来执行对应的postHandle和preHandle方法registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");}
}
- 4.发送请求:
- 5.拦截器拦截对应的路径下方法的调用,并在方法调用前后执行预先定义的方法:
拦截器执行流程:
3)拦截器参数(了解即可,主要用到的可能是前置处理)
- 前置处理,拿到handler(原始方法对象的封装)可以对原始方法进行一些处理
- 主要用处为在方法执行前作校验
4)拦截器链(了解即可)
- 当配置多个拦截器是,形参拦截器链
- 拦截器链的运行顺序参照拦截器添加的顺序为准
*注:上述内容均来自黑马程序员SSM框架的课程学习,仅用作学习交流,不作为商业用途,如有侵权,联系删除。