目录
Spring MVC 简介和体验
Spring MVC原理简单解析
Spring MVC涉及的组件
Spring MVC 快速体验
Spring MVC 接收数据
访问路径设置
接收参数(重点)
param和json参数比较
param参数接收
路径参数接收
json参数接收
@EnableWebMvc注解
接收Cookie和请求头数据
原生Api对象和共享域对象操作
Spring MVC 响应数据
handler方法分析
页面跳转控制
快速返回模板视图
转发和重定向
返回JSON数据(重点)
返回静态资源处理
RestFul风格设计
RESTFul风格特点
Spring MVC 其他拓展
全局异常处理机制
异常处理的两种方式
基于注解异常声明异常处理
拦截器使用
拦截器概念
拦截器使用
参数校验
- SSM为SpringFramework + SpringMVC + MyBatis
- Spring的3大特性:IoC(控制反转),DI(依赖注入),AOP(面向切面编程)。
- 框架 = jar + 配置文件
Spring MVC 简介和体验
Spring Web MVC 是一个基于Servlet API 构建的原始Web框架,一开始包含在Spring Framework下,目前普遍被选为JavaEE项目表述层开发的首选。
Spring MVC框架的两个核心功能:1.简化前端参数接收(形参列表)
2.简化后端数据响应(返回值)
Spring MVC原理简单解析
Spring MVC 框架内主要工作的有:
- DispatcherServlet:处理所有请求,用户的所有请求都由它接收
- HandlerMapping:缓存handler方法和地址,根据地址(如:/user/login)查找项目中的方法(如:UserController中的login方法)
- HandlerAdapter:适配器,真正进行参数和响应简化
- 视图解析器:查找视图页面,如果要返回的页面如:/WEB-INF/html/index.html,只需要返回“index”,它可以帮我们添加前缀和后缀,并查找页面信息并返回。
执行流程:
1.用户发送请求
2.请求到达DispatcherServlet
3.DispatcherServlet根据请求地址到HandlerMapping查找方法
4.HandlerMapping查找到handler方法并返回方法信息到DispatcherServlet。
5.DispatcherServlet根据handler方法信息将参数信息和方法信息发送到HandlerAdapter
6.HandlerAdapter进行简化参数处理,并调用handler方法
7.handler方法执行并返回数据给HandlerAdapter
8.HandlerAdapter接收数据并发送给DispatcherServlet
9.DispatcherServlet接收数据,如果接收的是页面地址则进行 操作10 ,不是则直接将数据返回给用户
10.视图解析器为页面添加前后缀并查找页面信息后返回给DispatcherServlet,DispatcherServlet将页面信息返回给用户。
Spring MVC涉及的组件
1.DispatcherServlet:Spring MVC提供,需要配置web.xml(Web项目配置文件)才能生效。
2.HandlerMapping:Spring MVC提供,需要进行IOC配置,将它加入到IOC容器才能生效,它内部缓存handler(controller的方法)和handler访问路径数据。
3.HandlerAdapter:Spring MVC提供,需要进行IOC配置,将它加入到IOC容器才能生效,它可以处理请求参数和处理响应数据。
4.Handler:handler又叫处理器,它是controller层方法的简称,由我们自己定义,接收参数,调用业务方法,返回数据。
5.ViewResolver(视图解析器):Spring MVC提供,需要进行IOC配置,将它加入到IOC容器才能生效,简化视图页面查找,前后端分离后,后端只需要返回JSON数据,不再需要视图解析器。
Spring MVC 快速体验
场景:项目启动后,用户访问/springmvc/hello服务器响应“hello springmvc!!”
1.创建项目ssm-springmvc-quick并转为web项目
2.导入项目所需的依赖:
- ioc:spring-context
- webmvc:spring-webmvc
- servlet:jakarta.jakartaee-web-api
3.创建一个controller类:HelloController
@Controller public class HelloController {@RequestMapping("springmvc/hello")//用户访问地址@ResponseBody//直接返回数据,不需要经过视图解析器public String hello(){System.out.println("Hello , Spring MVC!");return "hello springmvc";} }4.创建spring配置类
//将controller配置到ioc容器 //将handlerMapping handlerAdapter加入到ioc容器 @Configuration @ComponentScan("com.qiu.controller") public class MvcConfig {@Beanpublic RequestMappingHandlerMapping handlerMapping(){return new RequestMappingHandlerMapping();}@Beanpublic RequestMappingHandlerAdapter handlerAdapter(){return new RequestMappingHandlerAdapter();} }5.初始化ioc容器,设置dispatcherServlet访问路径(就是想要访问项目应该有的地址前缀)
以前我们需要在web.xml下操作:
<servlet><servlet-name>ds</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping><servlet-name>ds</servlet-name><url-pattern>/</url-pattern><!--访问路径--> </servlet-mapping>但现在可以创建一个用来初始化web项目的类,让他继承
AbstractAnnotationConfigDispatcherServletInitializer,项目启动后会自动扫描他,该类的作用是扫描Spring配置类来初始化ioc容器和设置DispatcherServlet的访问路径
//继承AbstractAnnotationConfigDispatcherServletInitializer类 //用来替换web.xml,会被web项目自动加载,会初始化ioc容器,会设置dispatcherServlet的访问地址 public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {//service mapper 层的ioc容器@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[0];}//设置spring配置类 springmvc controller的ioc容器@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{MvcConfig.class};}//设置springmvc自带servlet的访问地址@Overrideprotected String[] getServletMappings() {return new String[]{"/"};} }6.部署项目
7.启动项目并访问项目localhost:8080/springmvc/hello
Spring MVC 接收数据
访问路径设置
之前:@WebServlet("必须以 / 开头") /user/login
现在:@RequestMapping(不必须以 / 开头) /user/login user/login /user/login/
1.精准地址 :[一个 | 多个] /user/login | {"/user/login","/user/login2"}
2.模糊地址:* 一层模糊 ** 任意层模糊
/user/* -> /user/a,/user/aaa 可以;/user/a/b 不可以。
/user/** -> /user/a,/user/a/b,/user/a/a/a/a
3.该注解可以使用在类上或方法上,最终访问路径为:类地址 + 方法地址
4.请求方式指定:
请求方式主要有:GET,POST,PUT,DELETE
方式1:@RequestMapping("login",method=RequestMethod.GET)
方式2:@GetMapping("login") ,该注解只能用在方法上
不指定时默认所有请求方法都可以
多个请求方法@RequestMapping("a",method={RequestMethod.GET,RequestMethod.POST})
如果有类注解,方法注解内可以不写路径,该方法的访问路径默认为类注解的路径
接收参数(重点)
param和json参数比较
1.参数编码
param参数会被编译为ASCII码,如name=john doe,会被编译为name=john%20doe;而JSON参数会被编译为UTF-8。
2.参数顺序
param参数没有顺序限制,但JSON参数有顺序限制。
3.数据类型
param类型仅支持字符串类型、数值类型和布尔类型;JSON参数则支持更复杂的类型:数组,对象等。
4.嵌套性
param参数不支持嵌套,JSON参数支持
param参数接收
1.直接接收
如果形参数名和传递参数名相同,即可自动接收。
2.@RequestParam注解
可以使用该注解将Servlet请求参数绑定到controller中的方法的参数上。
使用场景: 指定绑定的请求参数名,要求参数必须传递,为请求参数提供默认值。
演示
@Controller @RequestMapping("user") public class UserController {/*** 直接接收* 请求地址:/user/data1?name=qiu&age=18* 1.方法参数名和请求参数名一致,2.请求参数可以不传时*/@ResponseBody@RequestMapping("data1")public String test(String name, int age){System.out.println("name = " + name);System.out.println("age = " + age);return "name="+name+" age="+age;}/*** 注解传参* 指定任意的请求参数名,默认要求必须传递,* 如果设置为不必须传递则要设置默认。* /user/data2?account=qiu&age=18*/@ResponseBody@RequestMapping("data2")public String test2(@RequestParam("account") String name,@RequestParam(required = false,defaultValue = "1") int age){System.out.println("name = " + name + ", age = " + age);return "name=" + name +"&age="+age;}/*** 特殊值* 一个属性传多个值,直接使用集合接收* 必须使用@RequestParam,不然可能会把第一个ids当作集合ids直接传值,类型不同会报错* /user/data3?ids=1&ids=2&ids=3*/@ResponseBody@RequestMapping("data3")public String test3(@RequestParam("ids")List<String> ids){System.out.println("ids = " + ids);return ids.toString();}/*** 使用实体类接收值* 创建User类 类属性有name 和 age,必须要有set/get方法* 属性名必须等于参数名* /user/data4?name=qiu&age=18*/@ResponseBody@RequestMapping("data4")public String test4(User user){System.out.println("user = " + user);return user.toString();} }测试
路径参数接收
像/user/{name}/18,如果我们想接收name的值,则可以使用路径参数接收
步骤:1、设置动态路径
2、接收动态路径参数
// /user/data5/qiu/123 @ResponseBody @RequestMapping("data5/{name}/{password}")//设置动态路径 //获取路径参数 public String test5(@PathVariable("name") String username,@PathVariable String password){return username+password; }
json参数接收
前端传递JSON数据时,SpringMVC框架可以使用@RequestBody来将JSON数据转换为Java对象。
Java对象Person
@Data public class Person {private String name;private int age;private String gender; }
Controller类PersonController
@Controller @RequestMapping("json") public class PersonController {@RequestMapping("person")@ResponseBodypublic String test(@RequestBody Person person){return person.toString();} }
发送请求
结果会报415错误,因为java原生的api只能接收路径传参和param参数,不支持json参数
解决方法:1、导入json处理的依赖 2、为handlerAdapter配置json转化器
添加依赖
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.0</version> </dependency>
在spring配置类上添加@EnableWebMvc:为handlerAdapter配置了json转化器
这样请求就发送成功了
@EnableWebMvc注解
在spring配置类上添加该注解相当于在springxml配置文件添加了<mvc:annotation-driven/>
而<mvc:annotation-driven/>在底层会自动帮我们把json处理器添加到一个HandlerAdapter上,再把这个handlerAdapter和一个handlerMapping添加到ioc容器内。
因此当我们添加该注解后就不需要在spring配置类中手动添加HandlerAdapter和HandlerMapping了。
接收Cookie和请求头数据
在参数前添加@CookieValue和@RequestHeader就行
public String method(@CookieValue("cookie") String value, @RequestHeader("header")String value2){... }
原生Api对象和共享域对象操作
想要使用原生Api对象可以直接通过参数获得
@Autowiredprivate ServletContext context;public String getApi(HttpServletRequest request,HttpServletResponse response,HttpSession session){ // 要获取ServletContext // ServletContext [1.最大的配置文件,2.全局最大的共享域,3.核心api getRealPath] // 方式1:通过request,session获取ServletContext servletContext = request.getServletContext();ServletContext servletContext1 = session.getServletContext(); // 方式2:声明一个全局变量,然后再上面添加一个@Autowire // (ServletContext在程序启动时会自动添加到ioc容器内)return "";}
Spring MVC 响应数据
handler方法分析
handler方法其实就是我们自己创建的controller类下的各种方法
接收请求数据,我们通过handler方法的形参列表
返回数据响应,我们通过return关键字
页面跳转控制
快速返回模板视图
当使用前后端分离模式(当前主流)时,该功能不再被需要。
要把jsp等视图返回给用户,需要使用视图解析器。
因此要先把视图解析器加入ioc容器
@Configuration @ComponentScan("com.qiu.controller") @EnableWebMvc //WebMvcConfigurer 可以快速配置springmvc的组件 public class MvcConfig implements WebMvcConfigurer {// 将视图解析器加入到ioc容器内, // registry.jsp("/WEB-INF/views/",".jsp") 为handler返回的视图路径添加前后缀,其底层就是字符串拼接@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {registry.jsp("/WEB-INF/views/",".jsp");} }
编写index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Title</title> </head> <body> <%-- ${data}意思是在request域中获取key=data的value,在request.setAttribute("data","hello jsp")--%><font color="red">${data}</font> </body> </html>
编写handler方法
@Controller @RequestMapping("jsp") public class HelloController {@GetMapping("index")public String hello(HttpServletRequest request){//在请求域中设置data:hello jsp,为index.jsp文件内的${data}赋值request.setAttribute("data","hello jsp");//此handler没有@ResponseBody,// 所以将把“index”与视图解析器设置的前后缀拼接并查找文件并返回给用户return "index";}}
这样用户就可以获取到index.jsp
转发和重定向
直接通过以下代码和注释学习
@GetMapping("index") public String hello(HttpServletRequest request){//在请求域中设置data:hello jsp,为index.jsp文件内的${data}赋值request.setAttribute("data","hello jsp");//此handler没有@ResponseBody,// 所以将把“index”与视图解析器设置的前后缀拼接并查找文件并返回给用户return "index"; }/*** 转发和重定向依然不能添加@ResponseBody* 要转发时,需要在返回的地址前添加 forward:* 要重定向时,需要在返回的地址前添加 redirect:*/ @GetMapping("forward") public String forward(){return "forward:/jsp/index"; } //原本重定向的地址如果是项目内需要包含根路径, // 即 http://localhost:8080/mvcpro/jsp/index 应该返回 redirect:/mvcpro/jsp/index // 但springmvc内部做了优化,不需要根路径 @GetMapping("redirect") public String redirect(){return "redirect:/jsp/index"; }@GetMapping("baidu") public String baidu(){return "redirect:http://www.baidu.com"; }
返回JSON数据(重点)
接收JSON和返回JSON都需要导入jackson-databind依赖
@ResponseBody:返回JSON的注解,添加到类或方法上,不走视图解析器直接把数据返回给前端。
当我们想要返回JSON数据给前端时,先导入依赖,把JSON解析器添加到ioc容器中(使用@EnableWebMvc),然后在handler方法上添加@ResponseBody注解,最后直接把pojo类返回就行了,springmvc会自动把该类转换为JSON数据再发送给前端。
pojo类
@Data @AllArgsConstructor @NoArgsConstructor public class User {private String name;private String gender; }
handler方法
@Controller @ResponseBody @RequestMapping("json") public class JsonController {@GetMapping("user")public User json(){return new User("秋","男");}}
返回静态资源处理
如果项目结构像这样,
我们无法通过 …/images/OIP-C.jpg访问静态资源。(jsp属于动态资源)
原因:DispatcherServlet会接收所有请求,包括对静态资源的请求,并且他只会在HandlerMapping中根据路径查找对应的handler方法,而静态资源没有对应的handler方法。
解决办法:需要我们开启静态资源查找:
还是在 WebMvcConfigurer 接口下,有个 configureDefaultServletHandling 方法
//相当与开启了<mvc:default-servlet-handler/> @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();//开启 }
开启后dispatcherServlet在HandlerMapping中没有找到资源,会再拿着路径查找有没有对应的静态资源。
RestFul风格设计
RestFul:Http协议的标准使用方案和风格。
前端往后端发送请求时,需要考虑:
1.路径如何设计(如:/user/add?) 2.要使用哪种传递参数方案(param?json?path?)
3.要使用哪种请求方式(get?post?delete?put?)
RestFul就是解决这些问题的一种方法。
RESTFul风格特点
1.每一种URI代表一种资源(URI是名词)
设计URI时尽量不使用动词,如:要新增用户不用:POST /user/add,而是使用:POST /user; 要删除用户不用:DELETE /user/delete?id=1,而是使用:DELETE /user/1
2.客户端使用GET,POST,PUT,DELETE 4个表示操作方式的动作来对服务器资源进行操作:
GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
3.资源使用xml或JSON
Spring MVC 其他拓展
全局异常处理机制
异常处理的两种方式
异常处理一般分为:编程式异常处理(使用trycatch手动显示地处理) 和 声明式异常处理(将异常处理的逻辑从具体业务逻辑中脱离,通过配置等方式进行统一的管理和处理)。
基于注解异常声明异常处理
1.声明异常处理控制器类
2.声明异常处理handler方法
//该注解 = @ControllerAdvice + @ResponseBody
//@ControllerAdvice 代表当前类的异常处理controller
//异常发生时,会走此类写的handler,
@RestControllerAdvice
public class GlobalExceptionHandler {//发送异常 -》 ControllerAdvice -》@ExceptionHandler -》根据异常类调用方法@ExceptionHandler(ArithmeticException.class)public Object ArithmeticExceptionHandler(ArithmeticException e){//自定义处理异常return null;}@ExceptionHandler(Exception.class)public Object ExceptionHandler(Exception e){return null;}}
拦截器使用
拦截器概念
Filter过滤器
在javaweb项目中,可以使用Filter过滤器,当请求来到服务器时,会先经过过滤器的处理(登录保护,编码格式,权限处理),再到对应的目标类。
但SpringMVC中,Filter过滤器就不适用了,SpringMVC使用DispatcherServlet接收请求,当我们设置了Filter时,请求会在到达DispatcherServlet前经Filter处理,而SpringMVC内部细化流程Filter无法拦截。
HandlerInterceptor 拦截器(SpringMVC环境下推荐使用)
SpringMVC提供了HandlerInterceptor拦截器,它会在handlerAdapter调用handler之前和之后拦截以及整体处理之后拦截。
拦截器使用
1.创建拦截器类
public class MyInterceptor implements HandlerInterceptor {//在handler执行前拦截:编码格式设置,登录保护,权限处理/**** @param request 请求对象* @param response 响应对象* @param handler 我们要调用的方法对象* @return true 放行 false 拦截*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return HandlerInterceptor.super.preHandle(request, response, handler);}//在handler执行后触发,没有拦截机制(方法已执行,拦截无意义)// modelAndView 返回的视图和共享域对象// 可以进行敏感词检查@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}//在数据返回给客户端前拦截@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);} }
2.修改配置类添加拦截器
@Configuration @ComponentScan("com.qiu.controller") @EnableWebMvc //WebMvcConfigurer 可以快速配置springmvc的组件 public class MvcConfig implements WebMvcConfigurer {// 将视图解析器加入到ioc容器内, // registry.jsp("/WEB-INF/views/",".jsp") 为handler返回的视图路径添加前后缀,其底层就是字符串拼接@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {registry.jsp("/WEB-INF/views/",".jsp");}@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置方案1:拦截所有请求//registry.addInterceptor(new MyInterceptor());//配置方案2:指定地址拦截 /**:拦截路径下所有请求//registry.addInterceptor(new MyInterceptor())// .addPathPatterns("/user/**"); //配置方案3:排除拦截 registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/**").excludePathPatterns("/user/data"); } }
参数校验
场景:每次请求参数都需要判断是否为空和检查格式是否正确。
java通过了jsr303系列注解,只需要准备对应的实体类并在其属性上添加注解,当该实体类对象的属性的数据为空或者格式有误,就会报错。
使用
1.导入依赖
<!-- 校验注解 -->
<dependency><groupId>jakarta.platform</groupId><artifactId>jakarta.jakartaee-web-api</artifactId><version>9.1.0</version><scope>provided</scope>
</dependency><!-- 校验注解实现-->
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator-annotation-processor</artifactId><version>8.0.0.Final</version>
</dependency>
2.应用
@Data
public class User {//age 1 <= age < = 150@Min(10)private int age;//name 3 <= name.length <= 6@Length(min = 3,max = 10)private String name;//email 邮箱格式@Emailprivate String email;}
3.handler方法标记
@RestController
@RequestMapping("user")
public class UserController {/*** @Validated 代表应用校验注解! 必须添加!*/@PostMapping("save")public Object save(@Validated @RequestBody User user,//在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!BindingResult result){//判断是否有信息绑定错误! 有可以自行处理!if (result.hasErrors()){System.out.println("错误");String errorMsg = result.getFieldError().toString();return errorMsg;}//没有,正常处理业务即可System.out.println("正常");return user;}
}