springMVC基本知识及其应用原理

关于Spring MVC框架

作用

● Spring MVC是基于Spring框架基础之上的,主要解决了后端服务器接收客户端提交的请求,并给予响应的相关问题

组成

● MVC = Model + View + Controller,它们分别是:

– MAcocdeesls:O数bj据ec模t L型a,ye通r)常共由同业构务成逻辑层(Ser vice Layer)和数据访问层(Data

– View:视图

– Controller:控制器

– MVC为项目中代码的职责划分提供了参考

需要注意:Spring MVC框架只关心V - C之间的交互,与M其实没有任何关系。

Spring MVC的核心执行流程图如下所示:

● [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zu8lM0fP-1655136813287)(file:///C:\Users\彭\AppData\Local\Temp\ksohtml2380\wps20.jpg)]

响应正文

● 响应正文的定义:将处理请求的方法的返回值作为响应到客户端的正文数据

● 响应正文的做法:

– 在方法上添加@ResponseBody

– 或,在类上添加@ResponseBody

– 或,在类上使用@RestController取代@Controller

● 响应正文的格式:响应的结果通常需要包含多项数据,响应1个字符串并不便于表示这些数据,通常响应JSON格式的字符串

● 响应正文的原理:Spring MVC内置了一系列的转换器

(Converter),根据处理请求的方法的返回值不同,自动选取某个转换器,将方法的返回值转换为响应到客户端的数据,当方法的返回值没有匹配的默认转换器时,会自动使用jackson-databind的转换器

● 当需要响应JSON格式的正文时,你需要:

– 添加jackson-databind依赖

– 在Spring MVC配置类上添加@EnableWebMvc注解

– 自定义类,作为处理请求的方法的返回值类型

– 类的属性必须添加Setter & Getter

使得处理请求的方法是响应正文的

响应正文的结果类型

● 当响应正文时,只要方法的返回值是自定义的数据类型,则Spring MVC框架就一定会调用jackson-databind中的转换器,就可以将结果转换为 JSON格式的字符串

● 通常,在项目开发中,会定义一个“通用”的数据类型,无论是哪个控制器的哪个处理请求的方法,最终都将返回此类型;

例如:

public class JsonResult<T> {
private Integer state; // 业务返回码private String message; // 消 息private T data; // 数 据
// Setters & Getters private JsonResult() { }
public static JsonResult<Void> ok() { return ok(null);
}
public static <T> JsonResult<T> ok(T data) { JsonResult<T> jsonResult = new JsonResult<>(); jsonResult.state = State.OK.getValue(); jsonResult.data = data;
return jsonResult;
}
public static JsonResult<Void> fail(State state, String message) { JsonResult<Void> jsonResult = new JsonResult<>(); jsonResult.state = state.getValue();
jsonResult.message = message; return jsonResult;
}
public enum State { OK(20000), ERR_USERNAME(40400), ERR_PASSWORD(40600);
Integer value; State(Integer value) {
this.value = value;
}
public Integer getValue() { return value;
}}}

接收请求参数

在Spring MVC中,当需要接收客户端的请求参数时,只需要将各参数直接声明为处理请求的方法的参数即可

● 在声明参数时,你可以将参数声明为你期望的数据类型,Spring会自动的执行类型转换

– 经过网络传输得到的数据,最原始的类型都是String

– Spring会自动尝试转换类型,通常,类型必须是基础数据类型及其包装类、常用类型(例如String等)、Spring定义的类型等

– 如果自动转换类型失败,会抛出相应的异常

● 如果客户端提交的请求中根本没有匹配名称的参数,则以上获取到的值将是null

– 例如:http://localhost/user/login.do

● 如果客户端仅提交了参数名称,却没有值,则以上获取到的值将是""(长度为0 的字符串)

– 例如:http://localhost/user/login.do?username=&password=&age=

● 如果客户端提交了匹配名称的参数,并且值是有效的,则可以获取到值

– 例如:http://localhost/user/login.do?username=admin&password=1234&age=27

● 以上名称应该是由服务器端决定的,客户端需要根据以上名称来提交请求参数

● 当有必要的情况下,可以在以上各参数的声明之前添加@RequestParam注解,其作用主要有:

– 配置name属性:客户端将按照此配置的值提交请求参数,而不再是根据方法的参数名称来提交请求参数

– 配置required属性:是否要求客户端必须提交此请求参数,默认为true,如果不提交,则出现400错误,当设置为false时,如果不提交,则服务器端将此参数值视为 null

– 配置defaultValue属性:配置此请求参数的默认值,当客户端没有提交此请求参数时,视为此值

● 如果需要客户端提交的请求参数较多,可以将这些参数封装为自定义的数据类型,并将自定义的数据类型作为处理方法的参数即可

● 你也可以将多个请求参数区分开来,一部分直接声明为处理请求的方法的参数,另一部分封装起来。

● 理论上来说,由于一个个的声明请求参数更加简单并且直观,所以,当请求参数数量非常少时,应该使用这种做法,当参数较多,或参数数量可能调整(例如需求变化引起的调整),则应该使用封装的数据类型

● 在开发实践中,考虑到需要使用到的其它框架的特性,使用封装的做法更为常见

小结

● 你可以将请求参数一个个的声明为处理请求方法的参数,也可以将多个参数封装到一个自定义类中,使用自定义类作为处理请求的方法的参数,Spring MVC框架会自动接收客户端提交的请求参数,并用于调用你编写的处理请求的方法

● 在大部分情况下,推荐使用将参数封装到自定义类的做法

● 你需要保证非String类型的参数是Spring框架可以成功自动转换类型的,或者,对转换失败有进一步的处理,或可以接受转换失败带来的后果

RESTful

● RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以 使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源

● RESTful的设计风格的典型表现就是:将某些唯一的请求参数的值放在 URL中,使之成为URL的一部分,例如:

– https://www.zhihu.com/question/28557115

– 这个URL的最后一部分28557115应该就是这篇贴子的id值,而不是使用例如?id=28557115这样的方式放在URL参数中。

● 注意:RESTful只是一种设计风格,并不是一种规定,也没有明确的或统一的执行方式

● 在开发实践中,如果没有明确的要求,以处理用户数据为例,可以将URL 设计为:

– /users:查看用户列表

– /users/9527:查询id=9527的用户的数据

– /users/9527/delete:删除id=9527的用户的数据

● RESTful建议根据希望实现的数据操作不同而使用不同的请求方式:

– 通过此前的学习,你应该知道HTTP请求方式包括:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

– 查询:使用GET请求

– 新增:使用POST请求

– 修改:使用PUT请求

– 删除:使用DELETE请求

● 在开发实践中,仅使用GET、POST这2种请求方式的做法更为常见,主要因为:

– 某些业务可能非常复杂,可能同时需要执行增、删、改、查的多种数据操作,不便于界定使用POST、DELETE、PUT、GET请求方式中的哪一种

– 大部分开发者更习惯于只使用GET、POST这2种请求方式

● 通常,以查询为主要目标的请求使用GET请求方式,否则,使用POST请求方式

● 在RESTful风格的URL中,大多是包含了某些请求参数的值,在使用 Spring MVC框架时,当需要设计这类URL时,可以使用{名称}进行占位,并在处理请求的方法的参数列表中,使用@PathVariable注解请求参数,即可将占位符的实际值注入到请求参数中!

示例代码:

@GetMapping("/{id}/info.do")
public UserVO info(@PathVariable Long id) { System.out.println("即将查询 id = " + id + " 的用户的信息……");
UserVO userVO = new UserVO(); userVO.setUsername("cc"); userVO.setPassword("1234567890"); [userVO.setEmail("cc@qq.com](mailto:sss@qq.com)"); return userVO;

}

提示:在以上代码中,URL中使用的占位符是{id},则方法的参数名称也应该是id,就可以直接匹配上!如果无法保证这2处的名称一致,则需要在@PathVariable注解中配置占位符中的名称,例如:

@GetMapping("/{userId}/info.do")
public UserVO info(@PathVariable("userId") Long id) {
// ...
}

在使用{}格式的占位符时,还可以结合正则表达式进行匹配,其基本语法 是:

{占位符名称:正则表达式}
@GetMapping("/{id:[0-9]+}/info.do")

● 当设计成以上URL时,仅当占位符位置的是纯数字的URL才会被匹配上, 如果不是纯数字的刚出现404错误页面。

以上模式的多种不冲突的正则表达式是可以同时存在的,例如:

@GetMapping("/{id:[0-9]+}/info.do")
public UserVO info(@PathVariable Long id) { System.out.println("即将查询 id = " + id + " 的用户的信息……");
// ...
} 
@GetMapping("/{username:[a-zA-Z]+}/info.do")
public UserVO info(@PathVariable String username) { System.out.println("即将查询 用户名 = " + username + " 的用户的信息……");
// ...

还可以存在不使用正则表达式,但是URL格式几乎一样的配置,例如:

@GetMapping("/{id:[0-9]+}/info.do")
public UserVO info(@PathVariable Long id) { System.out.println("即将查询 id = " + id + " 的用户的信息……");
// ...
}
@GetMapping("/{username:[a-zA-Z]+}/info.do")  public UserVO info(@PathVariable String username) {
System.out.println("即将查询 用户名 = " + username + " 的用户的信息……");
// ...
}
// http://localhost:8080/springmvc01_war_exploded/user/list/info.do @GetMapping("/list/info.do")
public UserVO list() {
System.out.println("即将查询 用户的列表 的信息……");
// ...
}

执行时,如果使用/user/list/ info.do,则会匹配到以上代码中的最后一个方法,并不会因为这个URL还能匹配第2个方法配置的{username:[a-zA- Z]+}而产生冲突,所以,使用了占位符的做法并不影响精准匹配的路径

RESTful的小结

● RESTful是一种风格,并不是规范或标准

● RESTful的典型表现有:

​ – 将某些唯一的请求参数的值放在URL中,使之成为URL的一部分

​ – 根据希望实现的数据操作不同而使用不同的请求方式

● 将请求参数设计到URL中时,可以在@RequestMapping或相关注解配置URL时使用{名称}进行占位

●在处理请求的方法的参数列表中,为占位符对应的参数添加@PathVariable,Spring MVC会自动获取URL中的值注入到参数中

●当占位符中的名称与方法参数的名称不一致时,需要配置@PathVariable注解的参数

● 在URL的占位符中,可以使用正则表达式限制占位符的文本格式,不冲突的多个使用了正则表达式的相同格式URL可以共存

● 使用了正则表达式的占位符的,与不使用占位符的,相同格式的URL可以共存,且优先匹配到不使用占位符的

统一处理异常

● Spring MVC框架提供了统一处理异常的机制,使得特定种类的异常对应一段特定的代码,后续,当编写代码时,无论在任何位置,都可以将异常直接抛出,由统一处理异常的代码进行处理即可

  • 无论哪种异常(包括RuntimeException及其子孙类异常),只要没有显式的使用 try…catch语法进行捕获并处理,均视为抛出
  • 关于统一处理异常,需要自定义方法对异常进行处理,关于此方法:
  • 注解:必须添加@ExceptionHandler注解
  • 访问权限:应该是公有的
  • 返回值类型:可参考处理请求的方法的返回值类型
  • 法名称:自定义
  • 参数列表:必须包含1个异常类型的参数,并且可按需添加HttpServletRequest、 HttpServletResponse等少量特定的类型的参数,不可以随意添加参数

● 为保证更合理的处理异常,应该:

– 将处理异常的代码放在专门的类中

– 在此类上添加@ControllerAdvice注解

– 由于目前主流的响应方式都是“响应正文”的,则可以将@ControllerAdvice替换为 @RestControllerAdvice

● 处理的方法的参数中的异常类型,就是Spring MVC框架将统一处理的异常类型,例如将参数声明为Throwable类型时,所有异常都可被此方法进行处理!但是,在处理过程中,应该判断当前异常对象所归属的类型,以针对不同类型的异常进行不同的处理

● Spring MVC允许存在多个统一处理异常的方法,这些方法可以在不同的类中,只要处理的异常的类型不冲突即可(允许继承)

– 例如:如果有2个或多个方法都处理NullPointerException,是错误的

– 例如:如果同时存在2个方法,分别处理NullPointerException和 RuntimeException,是允许的

● 在开发实践中,通常都会有handleThrowable()方法(方法名是自定义的),以避免某个异常没有被处理而导致500错误!

– 此方法中应该输出异常的相关信息,甚至跟踪信息,否则,当程序运行此至处时,可能不便于观察、分析、记录出现异常

@ExceptionHandler注解还可用于配置被注解的方法能够处理的异常的类型,其效力的优先级高于在方法的参数上指定异常类型

● 在开发实践中,建议为每一个@ExceptionHandler配置注解参数,在注解参数中指定需要处理异常的类型,而处理异常的方法的参数类型只需要包含@ExceptionHandler配置的类型即可,甚至可以是Throwable

拦截器(Interceptor)

● 在Spring MVC框架中,拦截器是可以运行在所有控制器处理请求之前和之后的一种组件,并且,如果拦截器运行在控制器处理请求之前,还可以选择对当前请求进行阻止或放行。

● 注意:拦截器的目的并不是“拦截下来后阻止运行”,更多的是“拦截下来后执行某些代码”,其优势在于可作用于若干种不同请求的处理过程, 即写一个拦截器,就可以在很多种请求的处理过程中被执行。

● 只要是若干种不同的请求过程中都需要执行同样的或高度相似的代码,都可以使用拦截器解决,典型的例如验证用户是否已经登录等等。

需要使用拦截器时,需要自定义类,实现HandlerInterceptor接口,例如

public class LoginInterceptor implements HandlerInterceptor { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("LoginInterceptor.preHandle()"); return false;
} 
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("LoginInterceptor.postHandle()");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("LoginInterceptor.afterCompletion()");}}

每个拦截器都必须注册才会被启用,注册过程通过重写WebMvcConfigure接口中的addInterceptors()方法即可,例如:

@Configuration // 此注解不是必须的
@EnableWebMvc
@ComponentScan("cn.tedu.springmvc") // 必须配置在当前配置类,不可配置在Spring的配置类
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/user/login.do");
}
}
  • 当进行访问时,在浏览器窗口中将看到一片空白,在Tomcat控制台可以看到preHandle()方法已经执行
  • 当把拦截器中preHandle()方法的返回值改为true时,在Tomcat控制台可以看到依次执行了preHandle() -> 控制器中处理请求的方法 -> postHandle() -> afterCompletion()
  • preHandle()方法的返回值为true时,表示“放行”,为false时,表示 “阻止”

● 关于注册拦截器时的配置,使用链式语法可以先调用addInterceptor()方法添加拦截器,然后调用addPathPatter()方法添加哪些路径需要被拦截, 此方法的参数可以是String…,也可以是List,在编写路径值时,可以使用作为通配符,例如配置为/user/,则可以匹配/user/login.do、

/user/reg.do等所有直接在/user下的路径,但不能匹配/user/1/ info.do,如果需要匹配若干层级,必须使用2个连续的星号,例如配置为/user/**

● 一旦使用通配符,就有可能导致匹配的范围过大,例如配置为/user/**时, 还可以匹配到/user/reg.do(注册)和/user/login.do(登录),如果此 拦截器是用于“验证用户是否登录”的,则不应该对这2个路径进行处理, 那么,配置拦截器时,还可以在链式语法中调用excludePathPattern()方法,以添加“排除路径”(例外)

SpringMVC小结

– Spring MVC框架的作用:

​ 接收请求,响应结果,处理异常

​ 掌握创建基于Maven的运行在Tomcat的Webapp

– 认识基础的依赖项

​ spring-webmvc、javax.servlet-api、jackson-databind

–接收请求参数的方式:

​ 将请求参数直接声明在处理请求的方法的参数列表中

​ 将若干个请求参数进行封装,并将封装的类型声明在处理请求的方法的参数列表中

​ 如果是URL中的路径,则需要使用@PathVariable

– 掌握响应JSON格式的正文的做法:

  • ​ 处理请求的方法必须添加@ResponseBody,或当前控制器类添加@ResponseBody,或当前控制器类添加@RestController
  1. ​ 在Spring MVC配置类上添加@EnableWebMvc
  2. ​ 在项目的pom.xml中添加了jackson-databind
    础的依赖项

​ spring-webmvc、javax.servlet-api、jackson-databind

–接收请求参数的方式:

​ 将请求参数直接声明在处理请求的方法的参数列表中

​ 将若干个请求参数进行封装,并将封装的类型声明在处理请求的方法的参数列表中

​ 如果是URL中的路径,则需要使用@PathVariable

– 掌握响应JSON格式的正文的做法:

  • ​ 处理请求的方法必须添加@ResponseBody,或当前控制器类添加@ResponseBody,或当前控制器类添加@RestController
  1. ​ 在Spring MVC配置类上添加@EnableWebMvc
  2. ​ 在项目的pom.xml中添加了jackson-databind
  3. ​ 处理请求的方法返回自定义的数据类型
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值