前言
再此之前,我已经学习也发过了关系maven,SpringFramework以及Mybatis的文章了,
现在,我们开始来学习关于SpringMvc这个控制层框架。
一、SpringMvc
1、简介
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称( spring-webmvc
),但它通常被称为“Spring MVC”。
在没有SpringMvc该框架之前,我们都是使用servlet以及过滤器等来接收前端信息或者返回给用户信息,Springmvc可以帮助我们 1. 简化前端参数接收( 形参列表 )2. 简化后端数据响应(返回值),这也是Springmvc最主要的两个功能。
2、核心组件和调用流程
Spring MVC与许多其他Web框架一样,是围绕前端控制器模式设计的,其中中央 Servlet
DispatcherServlet
做整体请求处理调度!
除了DispatcherServlet
SpringMVC还会提供其他特殊的组件协作完成请求处理和响应呈现。
2.1 涉及组件的理解
- DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发![ CEO ]
- HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler![秘书]
- HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器![经理]
- Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果![打工人]
- ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的![财务]
自己的理解:
在我们前端访问页面时,需要访问一个地址,该访问页面会调用我们java中的一个方法(Handler),HandlerMapping 相当于将我们的访问地址与该Handler 映射,访问所有地址都会先经过Dispatcherservlet(CEO),去查HandlerMapping(秘书)有没有该地址对应的方法,如果有,接着HandlerAdapter(经理)服务,它是handler与dispatchersevlet的一个适配器,用来将我们的request内容变为我们handler可以直接接收的字符,同时也可以将handler返回的json等变回浏览器支持的格式,ViewResovler(财务)可以简化模板视图页面的查找,可以添加一个统一的前缀与后缀,比如前缀为 cky/ 后缀为.html 如果我们直接返回 login 就会自动拼接成cky/login.html 会自动去返回该模板页面。ViewResovler并不是必须的,如果后端不返回页面,就不需要视图解析器。
3、小案例快速体验
3.1场景需求
![在这里插入图片描述](https://img-blog.csdnimg.cn/11b2a36dcd1e476a9e2d5851e46bfb32.png
创建一个idea maven项目(注意导成web项目),编写handler方法,请求响应一个输出。
3.1.1 导入依赖
<properties>
<spring.version>6.0.6</spring.version>
<servlet.api>9.1.0</servlet.api>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- springioc相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- web相关依赖 -->
<!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 -->
<!--
在 Spring Web MVC 6 中,Servlet API 迁移到了 Jakarta EE API,因此在配置 DispatcherServlet 时需要使用
Jakarta EE 提供的相应类库和命名空间。错误信息 “‘org.springframework.web.servlet.DispatcherServlet’
is not assignable to ‘javax.servlet.Servlet,jakarta.servlet.Servlet’” 表明你使用了旧版本的
Servlet API,没有更新到 Jakarta EE 规范。
-->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>${servlet.api}</version>
<scope>provided</scope>
</dependency>
<!-- springwebmvc相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
3.1.2 controller声明
@Controller
public class HelloController {
//handlers
/**
* handler就是controller内部的具体方法
* @RequestMapping("/springmvc/hello") 就是用来向handlerMapping中注册的方法注解!
* @ResponseBody 代表向浏览器直接返回数据!
*/
@RequestMapping("/springmvc/hello")
@ResponseBody
public String hello(){
System.out.println("HelloController.hello");
return "hello springmvc!!";
}
}
3.1.3 核心配置类
声明springmvc涉及组件信息的配置类`
//TODO: SpringMVC对应组件的配置类 [声明SpringMVC需要的组件信息]
//TODO: 导入handlerMapping和handlerAdapter的三种方式
//1.自动导入handlerMapping和handlerAdapter [推荐]
//2.可以不添加,springmvc会检查是否配置handlerMapping和handlerAdapter,没有配置默认加载
//3.使用@Bean方式配置handlerMapper和handlerAdapter
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.atguigu.controller") //TODO: 进行controller扫
//WebMvcConfigurer springMvc进行组件配置的规范,配置组件,提供各种方法! 前期可以实现
public class SpringMvcConfig implements WebMvcConfigurer {
@Bean
public HandlerMapping handlerMapping(){
return new RequestMappingHandlerMapping();
}
@Bean
public HandlerAdapter handlerAdapter(){
return new RequestMappingHandlerAdapter();
}
}
3.1.4 环境搭建
对于使用基于 Java 的 Spring 配置的应用程序,建议这样做,如以下示例所示:
//TODO: SpringMVC提供的接口,是替代web.xml的方案,更方便实现完全注解方式ssm处理!
//TODO: Springmvc框架会自动检查当前类的实现类,会自动加载 getRootConfigClasses / getServletConfigClasses 提供的配置类
//TODO: getServletMappings 返回的地址 设置DispatherServlet对应处理的地址
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定service / mapper层的配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
/**
* 指定springmvc的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { SpringMvcConfig.class };
}
/**
* 设置dispatcherServlet的处理路径!
* 一般情况下为 / 代表处理所有请求!
*/
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
3.1.6 配置tomcat
"注意选择 Tomcat Server
之后又出现了Tomcat 8080 in use的问题
解决办法点击此处
3.1.7 测试
二、SpringMvc 接收参数
1.路径设置注解
package com.cky.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller //组件
@ResponseBody //直接返回给浏览器信息
@RestController //==Controller+ResponseBody
@RequestMapping("/user")
public class UserContorller {
@RequestMapping //什么都不加 表示直接使用类方法上的访问地址
public String index(){
return null;
}
@GetMapping("/login")
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login(){
return null;
}
@RequestMapping("/register")
public String register(){
return null;
}
}
注解:
/**
* RequestMapping 地址映射
* RequestMapping 对/的要求不高 比如/user/login 也可以写成 user/login 或者/user/login/
* WebSevrlet 开头就必须加/
* 1、精准地址:可以1个也可以多个 多个的时候 用{地址1,地址2...}
* 2、模糊地址
* * 表示一层模糊 **表示任意层模糊
* 比如 user/* ===》user/a user/b 等
* user/** ===》 user/a/b/... user 等
* 3、类上和方法上添加@ReqquestMapping 的区别:
* 类上 可以不加 加的话 表示一个通用的地址
* 方法上必须加 表示精准的访问地址
* 如果类上没有 则直接使用方法上的
* 如果类上有 访问地址就是类上+方法上的
* 4、请求方式指定
* get、post、put、delete
* 客户端->http->ds->handler
* 默认不加请求方式情况 是任何访问都可以
* 请求方式指定 比如: @RequestMapping(value = "/user/login",method = RequestMethod.POST)
* 可以多个 @RequestMapping(value = "/user/login",method = {RequestMethod.POST,RequestMethod.GET})
* 如果请求方式不正确,会返回405异常
* 5、注解进阶 这种只能用在方法上!!!
* get @GetMapping("/login")==》 @RequestMapping(value = "/login",method = RequestMethod.GET)
* post @PostMapping("/login")
* put @PutMapping("/login")
* delete @DeleteMapping("/login")
*/
2、param接收参数四种类型
2.1 直接接收值
直接接值 可传可不传 不传不会报错 传值必须与形参名相同
对于这种不用返回视图函数的 记得加上 @ResponseBody //表示我们直接向浏览器返回数据
否则浏览器会报错
@RequestMapping("emp/login")
public String login(String name,Integer age){
System.out.println("name:"+name+",age:"+age);
return "name:"+name+",age:"+age ;
}
2.2 @RequetParam
//@RequestParam(name/value=“重置参数名”,required="true/false" 【默认为true,当配置为false时,我们可以配置默认值】,defaultValue)
// 注解 默认是必须传值 当设置为false时 可以设置默认值
@RequestMapping("emp/register")
public String register(@RequestParam(value = "user",required = false,defaultValue = "root") String username, int pwd){
System.out.println("name:"+username+",pwd:"+pwd);
return "name:"+username+",pwd:"+pwd ;
}
2.3 一名多值
参数上必须要加上@RequestParam,不然就会只将一个值 直接传给列表接收
//一key 多值
@RequestMapping("emp/test")
public String test(@RequestParam List<String> hbs){
System.out.println(hbs);
return "ok";
}
2.4 实体类型
首先要有实体类 有get和set方法
package com.cky.pojo;
import lombok.Data;
@Data
public class Employee {
private String name;
private int age;
}
//实体类接收
@RequestMapping("emp")
public String employee(Employee employee){
System.out.println(employee);
return employee.getName();
}
}性来传值,实体类属性可以直接再实体类上设置默认值。
3、路径参数接收
比如 user/root/123456
root 和123456 是我们要接收的参数
在这里 我们就要设置动态路径参数 {key}代表这里是路径参数
参数上 要加@PathVariable 注解
证明接收的是路径参数 否则会按照@Param参数来接值
该注解 也可以重置参数名 以及可以确定是否必须传参数 以及默认值
传入时必须按照实体类属 //路径参数接收
@RequestMapping("path/{name}/{pwd}")
public String path(@PathVariable String name,@PathVariable Integer pwd){
System.out.println("name:"+name+",pwd:"+pwd);
return "name:"+name+",pwd:"+pwd;
4、json格式接收参数
前端传递 JSON 数据时,Spring MVC 框架可以使用 @RequestBody
注解来将 JSON 数据转换为 Java 对象。@RequestBody
注解表示当前方法参数的值应该从请求体中获取,并且需要指定 value 属性来指示请求体应该映射到哪个参数上。
其使用方式和示例代码如下:
1、前端发送 JSON 数据的示例:(使用postman测试)
{
"name": "张三",
"age": 18,
"gender": "男"
}
2、定义一个用于接收 JSON 数据的 Java 类,例如:
@Data
public class Person {
private String name;
private int age;
private String gender;
// getter 和 setter 略
}
3、创建一个handler
在控制器中,使用 @RequestBody 注解来接收 JSON 数据,并将其转换为 Java 对象,例如:
package com.cky.controller;
import com.cky.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class PersonController {
@PostMapping("/json/data")
//RequestBody 代表我们接收的是信息体
//java并不接收 json的数据格式 我们需要配置
public String test(@RequestBody Person person){
System.out.println(person);
return person.getName()+person.getGender();
}}
4、配置json转换器
java原生并不接收json数据,我们需要进行配置,
让Handleradapter 可以进行json转换
首先导入json依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
接着要在配置类上加上**@EnableWebMvc**注解。效果等同于在 XML 配置中,可以使用 <mvc:annotation-driven>
元素!我们来解析<mvc:annotation-driven>
对应的解析工作!
@EnableWebMvc注解表示 让HandlerAdapter配置了json转化器
5、结果
5、@EnableWebMvc 注解
在配置类上添加上 该注解,效果等同于在 XML 配置中,可以使用 <mvc:annotation-driven>
元素!
使用该注解 就等同于同时添加上了 我们所谓的经理Handleradapter和秘书Handlermapping,同时为经理添加上了json转换器,写上该注解之后,其实配置类中就不用导入第三方经理和秘书的类。
可省略:
@Bean
public HandlerAdapter HandlerMapping(){
return new RequestMappingHandlerAdapter();
}
@Bean
public HandlerMapping handlerMapping(){
return new RequestMappingHandlerMapping();
}
6、接收请求头和cookie
6.1 接收请求头
接收请求头 要加上注解 @RequestHeader(xxx) xxx代表我们要接收请求头的内容
@Controller
@ResponseBody
public class HeaderContorller {
@RequestMapping("header")
public String cookie(@RequestHeader("Host") String host){
return host;
}
}
6.2 接收Cookie
可以使用 @CookieValue
注释将 HTTP Cookie 的值绑定到控制器中的方法参数。
@RequestMapping("save")
public String save(HttpServletResponse httpServletResponse){
Cookie cookie=new Cookie("username","root");
httpServletResponse.addCookie(cookie);//保存了一个cookie 名为username 值为root
return "ok";
}
@RequestMapping("cookie")
//通过@CookieValue 来获取相应cookie值的内容
public String cookie(@CookieValue("username") String name){
return name;
}
7、原生API的获取
比如java的一些原生api,我们可以在形参列表中自动获取
比如:
@Autowired //直接注入 ioc容器获取该类的组件对象 装配到ioc容器中
private ServletContext servletContext;
public String apiget(HttpServletResponse httpServletResponse, HttpServletRequest httpServletRequest, HttpSession httpSession){
//接下来要进行什么操作 直接使用即可
httpSession.getServletContext();
httpServletRequest.getServletContext();
return null;
}
/**对于servletContext 我们如何获取呢?
* 方案1: 使用request/session获取
* httpSession.getServletContext();
* httpServletRequest.getServletContext();
* 方案2
* 直接全局注入
*/
springmvc会在初始化容器的时候,讲servletContext对象存储到ioc容器中!
8、共享域对象操作
8.1 属性共享域回顾
在 JavaWeb 中,共享域指的是在 Servlet 中存储数据,以便在同一 Web 应用程序的多个组件中进行共享和访问。常见的共享域有四种:ServletContext
、HttpSession
、HttpServletRequest
、PageContext
。
ServletContext
共享域:ServletContext
对象可以在整个 Web 应用程序中共享数据,是最大的共享域。一般可以用于保存整个 Web 应用程序的全局配置信息,以及所有用户都共享的数据。在ServletContext
中保存的数据是线程安全的。HttpSession
共享域:HttpSession
对象可以在同一用户发出的多个请求之间共享数据,但只能在同一个会话中使用。比如,可以将用户登录状态保存在HttpSession
中,让用户在多个页面间保持登录状态。HttpServletRequest
共享域:HttpServletRequest
对象可以在同一个请求的多个处理器方法之间共享数据。比如,可以将请求的参数和属性存储在HttpServletRequest
中,让处理器方法之间可以访问这些数据。(一次请求,多次转发,只有在服务器端跳转才有用)PageContext
共享域:PageContext
对象是在 JSP 页面Servlet 创建时自动创建的。它可以在 JSP 的各个作用域中共享数据,包括pageScope
、requestScope
、sessionScope
、applicationScope
等作用域。
共享域的作用是提供了方便实用的方式在同一 Web 应用程序的多个组件之间传递数据,并且可以将数据保存在不同的共享域中,根据需要进行选择和使用。
setAttribute()
getAttribute()