SpringMVC简介:
SpringMVC概述:SpringMVC技术与Servlet技术功能等同,均属于web层开发技术
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
优点:使用简单,开发便捷(相比于Servlet)
灵活性强
SpringMVC入门案例:
1、使用SpringMVC技术需要导入SpringMVC坐标与Servlet坐标
<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>
2、创建SpringMVC控制器类(等同于Servlet功能)
//2、定义Controller //2.1、使用@Controller定义bean @Controller public class UserController { //2.2设置当前操作的访问路径 @RequestMapping("/save") //2.3设置当前操作的返回值类型 @ResponseBody public String save(){ System.out.println("user save..."); return "{'module':'springmvc'}"; } }
3、初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
//3、创建springmvc的配置文件,加载controller对应的bean @Configuration @ComponentScan("com.zsb") public class SpringMvcConfig { }
4、初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
//4、定义一个servlet容器启动的配置类,在里面加载spring的配置 public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer { //加载springMVC容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } //设置哪些请求归属SpringMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //加载spring容器配置 @Override protected WebApplicationContext createRootApplicationContext() { return null; } }
@Controller 类注解 SpringMVC控制器类定义上方 设定SpringMVC的核心控制器bean
@RequestMapping 方法注解 SpringMVC控制器方法定义上方 设置当前控制器方法请求访问路径
@ResponseBody 方法注解 SpringMVC控制器方法定义上方 设置当前控制器方法响应内容为当前返回值,无需解析
SpringMVC入门程序开发总结(1+N)
一次性工作
创建工程,设置服务器,加载工程
导入坐标
创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
多次工作
定义处理请求的控制器类
定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
入门案例工作流程分析
启动服务器初始化过程:
1、服务器启动,执行ServletContainerInitConfig类,初始化web容器
2、执行createServletApplicationContext方法,创建了WebApplicationContext对象
3、加载SpringMvcConfig
4、执行@ComponentScan加载对应的bean
5、加载UserController,每个@RequestMapping的名称对应一个具体的方法
6、执行getServletMappings方法,定义所有的请求都通过SpringMVC
单次请求过程:
1、发送请求localhost/save
2、web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
3、解析请求路径/save
4、由/save匹配执行对应的方法save()
5、执行save()
6、检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
Controller加载控制与业务bean加载控制:
SpringMVC相关bean(表现层dao)
Spring控制的bean
业务bean(Service)
功能bean(DataSource等)
因为功能不同,如何避免Spring错误的加载到:
SpringMVC的bean——加载Spring控制的bean的时候排除掉SpringMVC控制的bean
SpringMVC相关bean加载控制
SpringMVC加载的bean对应的包均在com.zsb.controller包内
Spring相关bean加载控制
方式一:Spring加载的bean设定扫描范围为com.zsb,排除掉controller包内的bean
方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中
@ComponentScan 类注解 属性:excludeFilters 排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
@Configuration @ComponentScan({"com.zsb.service", "com.zsb.dao"}) @ComponentScan(value = "com.zsb", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class )) public class SpringConfig { }
简化开发:
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringMvcConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
PostMan简介:
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件
作用:常用于进行接口测试
特征:简单 实用 美观 大方
PostMan基本使用:
注册登录 创建工作空间/进入工作空间 发起请求测试结果
请求与响应:
请求映射路径 @RequestMapping 方法注解、类注解 SpringMVC控制类方法定义上方 设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀 范例:
@Controller @RequestMapping("/book") public class BookController { @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("book save..."); return "{'module':'book save'}"; } }
属性:value(默认):请求访问路径,或访问路径前缀
乱码处理:post乱码处理
在ServletContainersInitConfig类中添加
@Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; }
请求参数
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
@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("/commonParamDifferentNmae") @ResponseBody public String commonParamDifferentNmae(@RequestParam("name") String userName, int age){ System.out.println("普通参数传递 userName ==>" + userName); System.out.println("普通参数传递 age ==>" + age); return "{'module':'common param different name'}"; }
@RequestParam 形参注解 SpringMVC控制器方法形参定义前面 绑定请求参数与处理器方法形参间的关系
参数:required:是否为必传参数 defaultValue:参数默认值
pojo参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接参数
@RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("普通参数传递 user ==>" + user); return "{'module':'pojo param'}"; }
嵌套pojo参数:POJO对象中包含POJO对象
public class User { private String name; private int age; private Address address; public Address getAddress() { return address; }
public class Address { private String province; private String city;
@RequestMapping("/pojoContainPojoParam") @ResponseBody public String pojoContainPojoParam(User user){ System.out.println("普通参数传递 user ==>" + user); return "{'module':'pojo contain pojo param'}"; }
数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数
@RequestMapping("/arrayParam") @ResponseBody public String arrayParam(String[] likes){ System.out.println("普通参数传递 likes ==>" + Arrays.toString(likes)); return "{'module':'array param'}"; }
集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
@RequestMapping("/listParam") @ResponseBody public String listParam(@RequestParam List<String> likes){ System.out.println("普通参数传递 likes ==>" + likes); return "{'module':'array param'}"; }
接收请求中的json数据
1、添加 json数据转换相关坐标:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
2、设置发送json数据(请求body中添加json数据):
3、开启自动转换json数据的支持:
@Configuration @ComponentScan("com.zsb.controller") @EnableWebMvc public class SpringMvcConfig { }
4、设置接收json数据:
// 集合参数:json格式 @RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> list){ System.out.println("list common(json)参数传递 list ==>" + list); return "{'module':'list common for json param'}"; }
@EnableWebMvc 配置类注解 SpringMVC配置类定义上方 开启SpringMVC多项辅助功能
@RequestBody 形参注解 SpringMVC控制器方法形参定义前面 将请求中请求体所包含的数据传递给请求参数,次注解一个处理器方法只能使用一次
// 集合参数:json格式 @RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> list){ System.out.println("list common(json)参数传递 list ==>" + list); return "{'module':'list common for json param'}"; }
请求参数(传递json数组)
POJO集合参数:json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数
// 集合参数:json格式 @RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> list){ System.out.println("list common(json)参数传递 list ==>" + list); return "{'module':'list common for json param'}"; }
@RequestBody和@RequestParam区别
区别:@RequestParam用于接收url地址传参,表单传参[application/x-www-form-urlencoded]
@RequestBody用于接收json数据[application/json]
应用:后期开发中,发送json格式数据为主,@RequestBody应用较广
如果发送非json数据,选用@RequestParam接收请求参数
日期类型参数传递
日期类型数据基于系统不同格式也不尽相同
接收形参时,根据不同的日期格式设置不同的接收方式
// 日期参数 @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("参数传递 data ==>" + date); System.out.println("参数传递 data1(yyyy-mm-dd) ==>" + date1); System.out.println("参数传递 data2(yyyy-mm-dd HH:mm:ss) ==>" + date2); return "{'module':'data param'}"; }
@DateTimeFormat 形参注解 SpringMVC控制器方法形参前面 设定日期时间类数据格式 范例:
@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("参数传递 data ==>" + date); System.out.println("参数传递 data1(yyyy-mm-dd) ==>" + date1); System.out.println("参数传递 data2(yyyy-mm-dd HH:mm:ss) ==>" + date2); return "{'module':'data param'}"; }
属性:pattern:日期时间格式字符串
类型转换器
Converter接口
public interface Converter<S,T> {
@Nullable
T convert(S var1);
}
请求参数年龄数据(String -> Interger)
日期格式转换(String -> Date)
@EnableWebMvc功能之一:根据类型匹配对应的类型转换器
响应
响应页面
//响应界面/跳转页面 @RequestMapping("/toJumpPage") public String toJumpPage(){ System.out.println("跳转页面"); return "hello.jsp"; }
响应数据
文本数据
//响应文本数据 @RequestMapping("/toText") @ResponseBody public String toText(){ System.out.println("返回文本数据"); return "response text"; }
json数据
//响应POJO对象 @RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){ System.out.println("返回json对象数据"); User user = new User(); user.setName("hello"); user.setAge(15); return user; } //响应POJO集合对象 @RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList(){ System.out.println("返回json集合数据"); User user1 = new User(); user1.setName("hello"); user1.setAge(15); User user2 = new User(); user2.setName("wuhu"); user2.setAge(18); List<User> list = new ArrayList<>(); list.add(user1); list.add(user1); return list; }
@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
注意事项:上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范。
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts...
RESTful入门案例
1、设定http请求动作(动词):
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id){ System.out.println("user delete..." + id); return "{'module':'user delete'}"; }
2、设定请求参数(路径变量):
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id){ System.out.println("user delete..." + id); return "{'module':'user delete'}"; }
@RequestMapping 方法注解 SpringMVC控制器方法定义上方 设置当前控制器方法访问路径
属性:value(默认):请求访问路径
method:http请求动作,标准动作(GET/POST/PUT/DELETE)
@RequestBody @RequestParam @PathVariable
区别:
@RequestParam用于接收url地址传参或表单传参
@RequestBody用于接收json数据
@PathVariable用于接收路径参数,使用{参数名称}描述路径参数
应用:
后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
采用RESTful进行开发,当参数数量较少时,例如1个,可以采用 @PathVariable接收请求路径变量,通常用于传递id值
@RestController 类注解 基于SpringMVC的RESTful开发控制器类定义上方 设置当前控制器为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能 范例
@RestController @RequestMapping("/books") public class BookController { }
@GetMapping @PostMapping @PutMapping @DeleteMapping 方法注解
基于SpringMVC的RESTful开发控制器方法定义上方 设置当前控制器方法请求访问路径与请求动作,例如@GetMapping对应GET请求 范例:
@GetMapping public String getAll(){ System.out.println("book getAll..."); return "{'module':'book getAll'}"; }
属性:value(默认):请求访问路径
案例:基于RESTful页面数据交互
1、制作SpringMVC控制器,并通过PostMan测试接口功能
@RestController @RequestMapping("/books") public class BookController { @PostMapping public String save(@RequestBody Book book){ System.out.println("book save ==>" + book); return "{'module':'book save success'}"; } @GetMapping public List<Book> getAll(){ System.out.println("getAll is running..."); Book book1 = new Book(); book1.setType("计算机"); book1.setName("SpringMVC入门案例"); book1.setDescription("小试牛刀"); Book book2 = new Book(); book2.setType("计算机"); book2.setName("SpringMVC实战教程"); book2.setDescription("一代宗师"); List<Book> list = new ArrayList<>(); list.add(book1); list.add(book2); return list; } }
2、设置对静态资源的访问放行
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { //当访问/pages/???时候不要走mvc,走/pages目录下的内容 registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
3、前端页面通过异步提交访问后台控制器
//添加 saveBook () { axios.post("http://localhost:8080/springmvc_07_rest_case/books",this.formData).then((res)=>{ }); }, //主页列表查询 getAll() { axios.get("http://localhost:8080/springmvc_07_rest_case/books").then((res)=>{ this.dataList = res.data; }); },
SSM整合
表现层数据封装:
前端接收数据格式——封装操作结果到code属性中
前端接收数据格式——创建结果模型类,封装数据到data属性中
前端接收数据格式——封装特殊消息到message(msg)属性中
设置统一数据返回结果类
public class Result{
private Object data;
private Integer code;
private String msg;
}
注意事项:Result类中的字段并不是固定的,可以根据需要自行增减。提供若干个构造方法,方便操作
异常处理器:
程序开发过程中不可避免的会遇到异常现象
出现异常现象的常见位置与常见诱因如下:
框架内部抛出的异常:因使用不合规导致
数据层抛出的异常:因外部服务器故障导致(如:服务器访问超时)
业务层抛出的异常:因业务逻辑书写错误导致(如:遍历业务书写操作,导致索引异常等)
表现层抛出的异常:因数据收集、校验等规则导致(如:不匹配的数据类型导致异常)
工具类抛出的异常:因工具类书写不严谨不够健壮导致(如:必要时放的连接长期未释放等)
集中的、统一的处理项目中出现的异常
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) public Result doException(Exception ex){ System.out.println("捕捉到异常"); return new Result(666, null, "捕捉到异常"); } }
@RestControllerAdvice 类注解 Rest风格开发的控制器增强类定义上方 为Rest风格开发的控制器类做增强 说明:此注解自带@ResponseBody注解与@Component注解,具备对应的功能
@ExceptionHandler 方法注解 专用于异常处理的控制器方法上方 设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行 说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
项目异常处理方案
业务异常(BusinessException):
规范的用户行为产生的异常
不规范的用户行为操作产生的异常
处理方案:发送对应消息传递给用户,提醒规范操作
系统异常(SystemException):
项目运行过程中可预计且无法避免的异常
处理方案:发送固定消息传递给用户,安抚用户
发送特定消息给运维人员,提醒维护
记录日志
其他异常(Exception):
编程人员未预期到的异常
处理方案:发送固定消息传递给用户,安抚用户
发送特定消息给编程人员,提醒维护(纳入预期范围内)
记录日志
1、自定义项目系统级异常
2、自定义项目业务级异常
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(BussinessException.class) public Result doBussinessException(BussinessException ex){ return new Result(ex.getCode(), null, ex.getMessage()); } @ExceptionHandler(SystemException.class) public Result doSystemException(SystemException ex){ //记录日志 //发送消息给运维 //发送邮件给开发人员,ex对象发给开发人员 return new Result(ex.getCode(), null, ex.getMessage()); } @ExceptionHandler(Exception.class) public Result doException(Exception ex){ return new Result(Code.SYSTEM_UNKNOWN_ERR, null, "系统繁忙,请稍后再试"); } }
3、自定义异常编码(持续补充)
public class Code { public static final Integer SAVE_OK = 20011; public static final Integer DELETE_OK = 20021; public static final Integer UPDATE_OK = 20031; public static final Integer GET_OK = 20041; public static final Integer SAVE_ERR = 20010; public static final Integer DELETE_ERR = 20020; public static final Integer UPDATE_ERR = 20030; public static final Integer GET_ERR = 20040; public static final Integer SYSTEM_ERR = 50001; public static final Integer SYSTEM_TIMEOUT_ERR = 50002; public static final Integer SYSTEM_UNKNOWN_ERR = 50002; public static final Integer BUSINESS_ERR = 60001; }
4、触发自定义异常
public Book getById(Integer id) { if (id == 1){ throw new BussinessException(Code.BUSINESS_ERR, "请不要用你的技术挑战我的耐心"); } //将可能会出现的异常进行包装,转换成自定义异常 try { int i = 1/0; } catch (Exception e) { throw new SystemException(Code.SYSTEM_TIMEOUT_ERR, "服务器访问超时", e); } Book book = bookDao.getById(id); return book; }
5、拦截并处理异常
拦截器
拦截器概念:
拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
作用:在指定的方法调用前后执行预先设定的代码
阻止原始方法的执行
拦截器与过滤器区别:
归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
拦截内容不同:FIlter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
拦截器入门案例:
1、声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean)
@Component public class ProjectInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); } }
2、定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Autowired private ProjectInterceptor projectInterceptor; @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); } @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books"); } }
3、添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个
@Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books"); }
4、使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)
拦截器参数:
前置处理:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); return true; }
参数
request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
返回值
返回值为false,被拦截的处理器将不执行
后置处理:
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); }
参数
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
完成后处理
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); }
参数
ex:如果处理器执行过程中出现异常对象,可以正对异常情况进行单独处理
多拦截器执行顺序
当配置多个拦截器时,形成拦截器链
拦截器链的运行顺序参照拦截器添加顺序为准
当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作