SpringBoot
SpringBoot
1.快速搭建
用IDEA创建一个springboot项目
参考博客:
如何在idea中创建一个SpringBoot项目(超详细教学)_idea创建springboot项目_叫我老伯的博客-CSDN博客
直接按照上面的方法写内容就行,依赖会自动下载。注意博客的world都拼错来
HelloWorld.java:
路径:demo\src\main\java\com\example\demo\controller
内容:
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//请求处理类
@RestController //返回json格式
public class HelloWorld {
@RequestMapping("/hello")
public String hello()
{
return "hello Spring Boot!!!";
}
}
2.再写一个引导类:HelloApplication.java
路径:demo\src\main\java\com\example\demo
内容:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*引导类 springboot项目的入口 运行main方法就可以启动项目
*/
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args){
SpringApplication.run(HelloApplication.class,args);
}
}
就可以运行了
通配符请求:
@GetMapping("/test/*")
public String test(){
return "通配符请求";
}
2.HTTP
2.1概述
HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则。
我们刚才初步认识了HTTP协议,那么我们在看看HTTP协议有哪些特点:
-
**基于TCP协议: ** 面向连接,安全
TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全
-
基于请求-响应模型: 一次请求对应一次响应(先请求后响应)
请求和响应是一一对应关系,没有请求,就没有响应
-
HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。
- 缺点: 多次请求间不能共享数据
- 优点: 速度快
请求之间无法共享数据会引发的问题:
- 如:京东购物。加入购物车和去购物车结算是两次请求
- 由于HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
- 发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据
2.2HTTP-请求协议
浏览器和服务器是按照HTTP协议进行数据通信的。
HTTP协议又分为:请求协议和响应协议
- 请求协议:浏览器将数据以请求格式发送到服务器
- 包括:请求行、请求头 、请求体
- 响应协议:服务器将数据以响应格式返回给浏览器
- 包括:响应行 、响应头 、响应体
在HTTP1.1版本中,浏览器访问服务器的几种方式:
| 请求方式 | 请求说明 |
|---|---|
| GET | 获取资源。 向特定的资源发出请求。例:http://www.baidu.com/s?wd=itheima |
| POST | 传输实体主体。 向指定资源提交数据进行处理请求(例:上传文件),数据被包含在请求体中。 |
| OPTIONS | 返回服务器针对特定资源所支持的HTTP请求方式。 因为并不是所有的服务器都支持规定的方法,为了安全有些服务器可能会禁止掉一些方法,例如:DELETE、PUT等。那么OPTIONS就是用来询问服务器支持的方法。 |
| HEAD | 获得报文首部。 HEAD方法类似GET方法,但是不同的是HEAD方法不要求返回数据。通常用于确认URI的有效性及资源更新时间等。 |
| PUT | 传输文件。 PUT方法用来传输文件。类似FTP协议,文件内容包含在请求报文的实体中,然后请求保存到URL指定的服务器位置。 |
| DELETE | 删除文件。 请求服务器删除Request-URI所标识的资源 |
| TRACE | 追踪路径。 回显服务器收到的请求,主要用于测试或诊断 |
| CONNECT | 要求用隧道协议连接代理。 HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 |
在我们实际应用中常用的也就是 :GET、POST
GET方式的请求协议:

-
请求行 :HTTP请求中的第一行数据。由:
请求方式、资源路径、协议/版本组成(之间使用空格分隔)- 请求方式:GET
- 资源路径:/brand/findAll?name=OPPO&status=1
- 请求路径:/brand/findAll
- 请求参数:name=OPPO&status=1
- 请求参数是以key=value形式出现
- 多个请求参数之间使用
&连接
- 请求路径和请求参数之间使用
?连接
- 协议/版本:HTTP/1.1
-
请求头 :第二行开始,上图黄色部分内容就是请求头。格式为key: value形式
- http是个无状态的协议,所以在请求头设置浏览器的一些自身信息和想要响应的形式。这样服务器在收到信息后,就可以知道是谁,想干什么了
常见的HTTP请求头有:
Host: 表示请求的主机名 User-Agent: 浏览器版本。 例如:Chrome浏览器的标识类似Mozilla/5.0 ...Chrome/79 ,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...)like Gecko Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有; Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页; Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。 Content-Type:请求主体的数据类型 Content-Length:数据主体的大小(单位:字节)
举例说明:服务端可以根据请求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处理不同的业务需求。
比如:
- 不同浏览器解析HTML和CSS标签的结果会有不一致,所以就会导致相同的代码在不同的浏览器会出现不同的效果
- 服务端根据客户端请求头中的数据获取到客户端的浏览器类型,就可以根据不同的浏览器设置不同的代码来达到一致的效果(这就是我们常说的浏览器兼容问题)
- 请求体 :存储请求参数
- GET请求的请求参数在请求行中,故不需要设置请求体
POST方式的请求协议:

- 请求行(以上图中红色部分):包含请求方式、资源路径、协议/版本
- 请求方式:POST
- 资源路径:/brand
- 协议/版本:HTTP/1.1
- 请求头(以上图中黄色部分)
- 请求体(以上图中绿色部分) :存储请求参数
- 请求体和请求头之间是有一个空行隔开(作用:用于标记请求头结束)
GET请求和POST请求的区别:
| 区别方式 | GET请求 | POST请求 |
|---|---|---|
| 请求参数 | 请求参数在请求行中。 例:/brand/findAll?name=OPPO&status=1 |
请求参数在请求体中 |
| 请求参数长度 | 请求参数长度有限制(浏览器不同限制也不同) | 请求参数长度没有限制 |
| 安全性 | 安全性低。原因:请求参数暴露在浏览器地址栏中。 | 安全性相对高 |
2.3 HTTP-响应协议
2.3.1格式
与HTTP的请求一样,HTTP响应的数据也分为3部分:响应行、响应头 、响应体

-
响应行(以上图中红色部分):响应数据的第一行。响应行由
协议及版本、响应状态码、状态码描述组成- 协议/版本:HTTP/1.1
- 响应状态码:200
- 状态码描述:OK
-
响应头(以上图中黄色部分):响应数据的第二行开始。格式为key:value形式
- http是个无状态的协议,所以可以在请求头和响应头中设置一些信息和想要执行的动作,这样,对方在收到信息后,就可以知道你是谁,你想干什么
常见的HTTP响应头有:
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg ; Content-Length:表示该响应内容的长度(字节数); Content-Encoding:表示该响应压缩算法,例如gzip ; Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒 ; Set-Cookie: 告诉浏览器为当前页面所在的域设置cookie ;
- 响应体(以上图中绿色部分): 响应数据的最后一部分。存储响应的数据
- 响应体和响应头之间有一个空行隔开(作用:用于标记响应头结束)
2.3.2响应状态码
| 状态码分类 | 说明 |
|---|---|
| 1xx | 响应中 --- 临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略 |
| 2xx | 成功 --- 表示请求已经被成功接收,处理已完成 |
| 3xx | 重定向 --- 重定向到其它地方,让客户端再发起一个请求以完成整个处理 |
| 4xx | 客户端错误 --- 处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 |
| 5xx | 服务器端错误 --- 处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等 |
参考: 资料/SpringbootWeb/响应状态码.md
关于响应状态码,我们先主要认识三个状态码,其余的等后期用到了再去掌握:
- 200 ok 客户端请求成功
- 404 Not Found 请求资源不存在
- 500 Internal Server Error 服务端发生不可预期的错误
2.4请求响应
2.4.1基础操作
1.下载postman模拟请求
在springboot项目里加入以下代码:
package com.example.demo.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
//
public class HelloController {
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request)
{
String name = request.getParameter("name");
String ageStr = request.getParameter("age");
int age = Integer.parseInt(ageStr);
System.out.println(name + ":" + age);
return "OK";
}
}
或者简化写法:
@RequestMapping("/simpleParam")
public String simpleParam(String name,Integer age)
{
System.out.println(name + ":" + age);
return "OK";
}
2.然后去postman里创建一个请求
这个是get的格式:
http://localhost:8080/simpleParam?name=Tom&age=10
这个是POST的格式:
http://localhost:8080/simpleParam
然后post需要更改请求体:
postman测试( POST请求 ):

结论:不论是GET请求还是POST请求,对于简单参数来讲,只要保证请求参数名和Controller方法中的形参名保持一致,就可以获取到请求参数中的数据值。
2.4.2 参数名不一致
可以使用Spring提供的@RequestParam注解完成映射
在方法形参前面加上 @RequestParam 然后通过value属性执行请求参数名,从而完成映射。代码如下:
package com.example.demo.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
// http://localhost:8080/simpleParam?name=Tom&age=20
// 请求参数名:name
//springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam("name") String username , Integer age ){
System.out.println(username+" : "+age);
return "OK";
}
}
注意事项:
@RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错
然后用上面的GET方式测试一下
2.5 参数
2.5.1实体参数
我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守如下规则:请求参数名与实体类的属性名相同
step1:创建一个跟controller同级的目录pojo,然后创建一个类叫User:
点alt+insert可以快速生成方法 点击getter and setter
package com.example.demo.controller;
import com.example.demo.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
// http://localhost:8080/simpleParam?name=Tom&age=20
// 请求参数名:name
//springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam("name") String username , Integer age ){
System.out.println(username+" : "+age);
return "OK";
}
@RequestMapping("/simplePojo")
public String simplePojo (User user){
System.out.println(user);
return "OK";
}
}
然后在controller添加以下代码:
@RequestMapping("/simplePojo")
public String simplePojo (User user){
System.out.println(user);
return "OK";
}
然后用postman测试:
http://localhost:8080/simplePojo?name=ITCAST&age=16
2.5.2数组集合参数
后端程序接收多个值的方式有两种:
- 数组
- 集合
1.数组
数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
Controller方法:
@RestController
public class HelloController {
//数组集合参数
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby){
System.out.println(Arrays.toString(hobby));
return "OK";
}
}
Postman测试:
http://localhost:8080/arrayParam?hobby=game&hobby=java
http://localhost:8080/arrayParam?hobby=game ,java
2.集合
集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
System.out.println(hobby);
return "OK";
}
postman测试类似数组
2.5.3日期参数
Controller方法:
@RequestMapping("/dateParam")
public String dateParam (@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime ){
//按照格式转化为updateTime
System.out.println(updateTime);
return "OK";
}
postman测试:
http://localhost:8080/dateParam?updateTime=2022-12-12 10:00:05
2.5.4JSON格式参数
选择post,选择raw的方式,然后在右边选择数据格式为json
{
"name":"ITCAST",
"age":16,
"address":{
"province":"北京",
"city":"北京"
}
}
@RequestBody把json封装成对象
Controller方法:
@RequestMapping("/jsonParam")
public String jsonParam (@RequestBody User user){
System.out.println(user);
return "OK";
}
如果返回的是一个对象,会转化为json
2.6统一响应结果
2.6.1统一响应
如果我们开发一个大型项目,项目中controller方法将成千上万,使用上述方式将造成整个项目难以维护。在真实的项目开发中,无论是哪种方法,我们都会定义一个统一的返回结果。
格式:
public class Result{
//响应码:1是成功,2是失败
private Integer code;
//提示信息
private String msg;
//返回的数据
private Object data;
}
改造:
@RequestMapping("/hello")
public Result hello(){
System.out.println("Hello World ~");
//return new Result(1,"success","Hello World ~");
return Result.success("Hello World ~");
}
//响应统一格式的结果
@RequestMapping("/getAddr")
public Result getAddr(){
Address addr = new Address();
addr.setProvince("广东");
addr.setCity("深圳");
return Result.success(addr);
}
//响应统一格式的结果
@RequestMapping("/listAddr")
public Result listAddr(){
List<Address> list = new ArrayList<>();
Address addr = new Address();
addr.setProvince("广东");
addr.setCity("深圳");
Address addr2 = new Address();
addr2.setProvince("陕西");
addr2.setCity("西安");
list.add(addr);
list.add(addr2);
return Result.success(list);
}
Springboot项目的静态资源(html,css,js等前端资源)默认存放目录为:classpath:/static 、 classpath:/public、 classpath:/resources
classpath就是resources,在里面创建static,public,resources名字的文件夹就可
2.6.2案例
step1:把需要的文件目录建立出来
step2:
EmpController内容
package com.example.demo.controller;
import com.example.demo.pojo.Emp;
import com.example.demo.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.itheima.pojo.Result;
import java.util.List;
@RestController
public class EmpController {
@RequestMapping("/listEmp")
public Result list(){
//加载并解析xml文件
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
//对数据进行转换处理
empList.stream().forEach(emp -> {
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}
else if("2".equals(gender)){
emp.setGender("女");
}
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}
else if("2".equals(job)){
emp.setJob("班主任");
}
else if("3".equals(job)){
emp.setJob("就业指导");
}
});
//响应数据
return Result.success(empList);
}
}
然后postman发送:
http://localhost:8080/listEmp
可以看到解析出来的数据
2.7分层解耦
2.7.1三层架构
上述案例的处理逻辑呢,从组成上看可以分为三个部分:
- 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
- 逻辑处理:负责业务逻辑处理的代码。
- 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。
按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:

- Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
- Service:业务逻辑层。处理具体的业务逻辑。
- Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
2.7.2 IOC&DI
任务:完成Controller层、Service层、Dao层的代码解耦
- 思路:
- 删除Controller层、Service层中new对象的代码
- Service层及Dao层的实现类,交给IOC容器管理@Component
- 为Controller及Service注入运行时依赖的对象@Autowired
- Controller程序中注入依赖的Service层对象
- Service程序中注入依赖的Dao层对象
修改完了之后:
EmpDaoA.java
@Component //当前类交给ioc容器管理 成为IOC容器的bean
public class EmpDaoA implements Empdao {
@Override
public List<Emp> listEmp() {
//加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
return empList;
}
}
EmpServiceA.java
@Component//当前类交给ioc容器管理 成为IOC容器的bean
public class EmpServiceA implements EmpService {
@Autowired//自动装配,按照类型(EmpDaoA)找到对应的,然后注入
private EmpDaoA empDao;
@Override
public List<Emp> listEmp() {
List<Emp>empList = empDao.listEmp();
empList.stream().forEach (emp -> {
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}
else if("2".equals(gender)){
emp.setGender("女");
}
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}
else if("2".equals(job)){
emp.setJob("班主任");
}
else if("3".equals(job)){
emp.setJob("就业指导");
}
});
return empList;
}
}
EmpController.java
@RestController
public class EmpController {
@Autowired //IOC容器回提供该类型的bean对象,并赋值给该变量 -依赖注入
private EmpService empService;
@RequestMapping("/listEmp")
public Result list(){
List<Emp> empList = empService.listEmp();
//响应数据
return Result.success(empList);
}
}
详解:
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
-
@Controller (标注在控制层类上)
-
@Service (标注在业务层类上)
-
@Repository (标注在数据访问层类上)
就是一般不用@Component 改成细分的 如果没有细分
给bean起名字:
@Repository("daoA") //当前bean叫daoA
public class EmpDaoA implements Empdao {
@Override
public List<Emp> listEmp() {
//加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
return empList;
}
}
注意事项:
- 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
- 使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
2.7.3组件扫描
使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描
@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,默认扫描的范围是SpringBoot启动类所在包及其子包。
2.7.4DI详解
@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)
入门程序举例:在EmpController运行的时候,就要到IOC容器当中去查找EmpService这个类型的对象,而我们的IOC容器中刚好有一个EmpService这个类型的对象,所以就找到了这个类型的对象完成注入操作。
但是有一种情况就是如果有多个一样类型的,就会报错,无法完成自动装配
如何解决上述问题呢?Spring提供了以下几种解决方案:
-
@Primary
-
@Qualifier
-
@Resource
使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。

使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。
- @Qualifier注解不能单独使用,必须配合@Autowired使用

使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

面试题 : @Autowird 与 @Resource的区别
- @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
- @Autowired 默认是按照类型注入,而@Resource是按照名称注入

浙公网安备 33010602011771号