快速入门
使用SpringBoot开发一个web应用, 浏览器发起请求后,给浏览器返回字符串"Hello World"
1.创建工程
专业版
专业版IDEA自带插件, 可以快速创建项目
社区版
社区版IEDA需要自己安装插件, 才能创建项目, 并且IDEA版本不高于2022
第三方网站
速度较快,官网打不开时备选 https://start.aliyun.com/
官网网站
官方网站可能比较慢,但是比较推荐 https://start.spring.io/
2.目录解析
简化代码
代码结构
1.启动类: SpringHelloWoreldApplication.java
2.配置文件:
3.依赖文件:
// 继承父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
// 项目信息
<groupId>com.itheima</groupId>
<artifactId>Day02_springboot_hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Day02_springboot_hello</name>
<description>Day02_springboot_hello</description>
// JDK版本
<properties>
<java.version>17</java.version>
</properties>
// 项目依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
// 项目插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
4.源代码文件:
3.入门程序
创建请求处理类
启动类所在包下, 添加controller包和类, 连包带类一次创建
编写代码
// 定义请求处理类
@RestController
public class hellocontroller {
// 定义请求处理方法
@RequestMapping("/hello")
public String hello(){
System.out.println("hello world~");
return "hello world~";
}
}}
测试运行
- 执行启动类的main方法
- 程序默认运行在8080端口
Http协议
超文本传输协议, 规定了浏览器和服务器之间数据传输的规则
特点
- 基于TCP协议, 面相连接,安全可靠
- 基于请求-响应模型, 一次请求对应一次响应
- HTTP协议是无状态的协议, 对于事务处理没有记忆能力, 每次请求响应都是独立的
- 缺点: 多次请求间不能共享数据
- 优点: 速度快
请求协议
规定了浏览器向服务器发送请求的格式
请求行: 请求数据的第一行, 包含 请求方式, 资源路径, 协议等信息
请求头: 第二行开始, 以 key: value 的格式携带客户端信息信息
请求空行: 用来分隔请求头和请求体
请求体: 存放请求的参数, 只有POST请求有请求体
请求头详解
GET和POST
- GET请求的参数放在请求行中, 没有请求体
- GET请求携带的参数是有大小限制的
- POST请求的参数放在请求体中
- POST请求大小是没有限制的
响应协议
规定了服务器向浏览器发送请求和格式
响应行: 响应数据的第一行, 包含协议, 状态码, 描述等信息
响应头: 从第二行开始, 以 key: value 的格式, 描述响应信息
响应空行: 分隔响应头和响应体
响应体: 存放响应的数据
响应码
状态码大全: 状态 | Status - HTTP 中文开发手册 - 开发者手册 - 腾讯云开发者社区-腾讯云
响应头
协议解析
浏览器自带HTTP解析程序, 可以自动封装请求, 解析响应
服务器也需要HTTP解析程序, 解析请求报文, 封装响应报文
web服务器原理:
- 通过ServerSocket接受HTTP请求,
- 通过输入流获取请求报文
- 根据请求协议解析请求报文
- 根据响应协议封装响应报文,
- 通过输出流给浏览器响应数据
常见的web服务器:
web服务器
web服务器是一个软件程序, 对HTTP协议的操作进行封装, 使得程序员不用直接对协议进行操作,让Web开发更加便捷, 主要就是用于部署项目, 对外提供网上信息浏览服务
基本使用
入门程序解析
Tomcat
Apache软件基金会开源的免费的轻量级的Web服务器
- 官网: Apache Tomcat® - Welcome!
- Tomcat也被称为web容器, servlet容器
- servlet程序需要依赖Tomcart才能运行
- 轻量级: 支持Server/JSP等少量的javaEE规范
- javaEE指java企业版
- javaEE规范就是企业级开发的技术规范总和
- javaEE规范总共13项, 包括 JDBC, JNDI, EJB, RMI, JSP, Servlet, XML, JMS, Java IDL, JTA, JavaMail, JAF, 其中有些规范已经过时
基本使用
下载
官网下载, 或者使用资料中的软件包
安装
绿色版软件, 直接解压皆可
卸载
删除软件文件即可
目录结构
启动
双击 bin/startup.bat 启动程序
关闭
- 点击 x 号强制关闭
- 双击 bin/shutdown.bat 正常关闭
- Ctrl+C 正常关闭
部署
把项目复制到webapps目录, 部署完成, 访问localhost:8080/dome/index.html
排错
- 启动窗口一闪而过: 检查JAVA_HOME环境变量是否正确配置
- 控制台中文乱码: 修改conf/logging.properties 文件中的编码格式 (第51行)
- 端口冲突: 配置Tomcat运行端口, conf/server.xml
- HTTP协议默认端口号为80, 如果将Tomcat端口改为80, 则将来访问Tomcat时, 将不用输入端口号
- 说明: springboot工程会内嵌Tomcat
SpingBoot程序解析
起步依赖
我们创建Spingbott项目,勾选web开发依赖, 实际就是引入官方的起步依赖
- spring-boot-web: 包含了web应用开发所需要的常见依赖
- spring-boot-test: 包含了单元测试所需要的常见依赖
- 官方提供的起步依赖, 聚合了web开发中常用的依赖, 只需要引入这个依赖, 就可以愉快的进行开发了
- 起步依赖并不需要指定版本, 因为起步依赖会继承, 因为所有的spingboot工程都要基础springboot父工程, 父工程会统一管理起步依赖的版本
内嵌Tomcat
基于SpringBoot创建的web应用程序, 内置了Tomcat服务器, 当启动类运行时, 会自动启动Tomcat服务器
请求响应
流程
概念: 浏览器.服务器架构模式,
执行过程
- 当通过启动类启动springboot程序时, 内置的tomcat服务器随之启动
- tomcat服务器的核心就是前端控制器
- 前端控制器提供了DispatcherServlet程序,
- 程序中的HttpServletRequest对象(请求对象)用来解析浏览器的请求,
- 浏览器的请求经过解析后会传给Controller控制器
- 在Controller中通过请求对象就可以拿到前端的请求数据
- 程序中的HttpServletResponse对象(响应对象)用来封装响应给浏览器的数据,
- Controller中的数据经过响应对象的封装响应给浏览器
- 有了前端控制器程序帮助我们和浏览器通信,
- 程序员就不必关心Http协议的解析和封装
- 而是专注于Controller控制器的业务开发
- 极大提高了我们的工作效率
重点说明
- 请求对象(HttpServletRequest): 获取请求数据
- 响应对象(HttpServletResponse): 设置响应数据
- BS架构: Browser/Server架构即 浏览器.服务器架构模式
- 客户端只需要浏览器, 应用程序的逻辑和数据都存在服务端口
- CS架构: Client/Server架构即 客户端/服务器架构模式
- 客户端根据不同设备下载使用, 客户端拥有更强的数据和处理处理能力
postman
当前项目主流的开发方式是前后端分类模式, 前后端基于接口文档进行协作开发, 后端要保证接口的可用, 就需要进行接口测试
postman就是专门用于接口测试的工具, 起初是Chrome插件, 发展成网页调试与发送HTTP请求的工具
其他工具:
基本使用
- 官网: Postman 下载及安装教程
- 下载: 官网下载或者使用提供好的安装包
- 安装: 傻瓜式安装
- 登录注册: 第一次使用先注册再登录
- 使用:
请求
目标: 获取各种请求参数
1.简单参数
1.1原始方式
在原始的web程序中, 获得请求参数, 需要通过HttpServletRequest对象手动获取
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request) {
// 获取请求参数
String nameStr = request.getParameter("name");
String ageStr = request.getParameter("age");
int age = Integer.parselInt(ageStr);
System.out.println("name:" + name);
System.out.println("age:" + age);
return "ok";
}
}
- 通过请求处理对象HttpServletRequest获取前端数据比较繁琐
- 而且拿到的都是字符串数据, 还需要手动类型转换
1.2 SpringBoot
a.定义同名形参
参数名称与形参变量名相同, 定义形参即可接收参数
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/simpleParam")
public String simpleParam(String name, Integer age) {
// 获取请求参数
System.out.println("name:" + name);
System.out.println("age:" + age);
return "ok";
}
}
b.修改形参映射
如果方法形参名称与请求参数名称不匹配, 可以使用@RequestParam完成映射
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam(name="username", required=false) String name, Integer age) {
// 获取请求参数
System.out.println("name:" + name);
System.out.println("age:" + age);
return "ok";
}
}
- username是前端传递的参数名, name是后端接收的参数名
- @RequestParam中的erquired属性默认为true, 表示参数必传,
- 如果参数是可选的, 设置为false即可
2.实体参数
2.1简单实体对象
规则: 请求参数名与形参对象属性名相同, 即可直接通过POJO接收
实体类
public class User
public String name;
public String age;
//生成get/set方法
//重写toString()方法
}
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/simplePojo")
public String simpleParam(User user) {
// 获取请求参数
System.out.println(user);
return "ok";
}
}
传参
2.2复杂实体对象
规则: 请求参数名与形参对象属性名相同, 按照对象层次结构关系即可接收嵌套POJO属性参数
实体类
public class User {
public String name;
public String age;
public Address address;
//生成get/set方法
//重写toString()方法
}
public class Address {
public String province;
public String city;
//生成get/set方法
//重写toString()方法
}
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/simplePojo")
public String simpleParam(User user) {
// 获取请求参数
System.out.println(user);
return "ok";
}
}
传参
3.数组集合参数
3.1数组
规则: 请求参数名称与形参中数组变量名相同, 就可以直接通过数组接收参数
作用: 把多个数据接收到数组中
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/arrayParam")
public String simpleParam(String[] hobby) {
// 获取请求参数
System.out.println(Arrays.toString(hobby));
return "ok";
}
}
传参
3.2集合
请求参数名称与形参中集合名称相同, 通过@RequestParam绑定参数关系
作用: 把多个数据接收到集合中
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/listParam")
public String simpleParam(@RequestParam ArrayList<String> hobby) {
// 获取请求参数
System.out.println(hobby);
return "ok";
}
}
传参
4.时间日期参数
规则: 使用@DateTimeFormat注解完成日期参数格式转换
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/dataParam")
public String dataParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
//接收时间类型的数据
System.out.println(updateTime);
return "ok";
}
}
传参
5. JSON参数
规则: JSON数据键名与形参对象属性名相同, 定义POJO类型参数即可接收参数, 使用@RequestBody标识
实体类
public class User {
public String name;
public String age;
public Address address;
//生成get/set方法
//重写toString()方法
}
public class Address {
public String province;
public String city;
//生成get/set方法
//重写toString()方法
}
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user) {
//接收JSON类型的数据
System.out.println(user);
return "ok";
}
}
传参
步骤: POST -> Body -> Json 或 raw
6.路径参数
规则: 通过请求Url直接传递参数, 使用 { } 来标识路径参数, 再使用@PathVariable来获取路径参数
方法
// 声明请求处理类
@RestController
public class RequestController {
// 声明请求处理方法
@RequestMapping("/simpleParam/{id}/{name}")
public String simpleParam(@PathVariable String id, @PathVariable String name) {
// 获取请求参数
System.out.println(id);
System.out.println(name);
return "ok";
}
}
传参
响应
目标: 设置响应数据
3.1设置响应
当我们使用 @RestController 注解, 声明请求处理类时, 类中的方法默认就会把返回值作为参数响应给前端
注解: @ResponseBody
类型: 方法注解,类注解
位置: Controller方法上/类上
作用: 将方法返回值直接响应, 如果返回值类型是 实体对象或集合, 将会转成JSON格式响应
说明: @RestController = @Controller + @ResponseBody
示例
// 声明请求处理类
@RestController
public class ResponseController {
//声明请求处理方法
@RequestMapping("/hello")
public String hello(){
//响应简单字符串
return "hello world";
}
}
// 声明请求处理类
@RestController
public class ResponseController {
//声明请求处理方法
@RequestMapping("/getAddr")
public Address getAddr(){
//响应对象信息
Address address = new Address();
address.setProvince("广东");
address.setCity("深圳");
return address;
}
}
// 声明请求处理类
@RestController
public class ResponseController {
//声明请求处理方法
@RequestMapping("/listAddr")
public List<Address> listAddr(){
//响应集合信息
List<Address> list = new ArrayList<Address>();
Address addr = new Address();
addr.setCity("广东");
addr.setProvince("广州");
Address addr2 = new Address();
addr2.setProvince("山东");
addr2.setCity("济南");
list.add(addr);
list.add(addr2);
return list;
}
}
3.2统一响应
如果后端的响应结果是随意返回的, 那么就会增加前端的处理成本, 所以需要设置统一的数据响应格式
引入Result.java类, 作为统一的响应结果
@RestController
public class ResponseController {
@RequestMapping("/hello")
public Result hello() {
//不管什么类型的数据, 都封装到Result中, 响应给前端
return Result.success("hello world");
}
}
案例
目标: 响应员工数据给前端,前端正确展示列表
实现步骤
- 在pom.xmll文件中引入dom4j依赖, 用于解析xml文件
- 引入工具类XMLParserUtils, 实体类Emp, xml文件emp.xml
- 引入前端静态资源, 放在resources下的static目录中
- springboot项目的静态资源默认存放目录为: classpath:/static或classpath:/public或classpath:/resources
- 编写controller程序, 处理请求, 解析xml文件, 把解析结果封装到实体类中, 响应给前端
package com.itheima.controller;
import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class EmpController {
@RequestMapping("/listEmp")
public Result list(){
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
//2. 对数据进行转换处理 - gender, job
empList.stream().forEach(emp -> {
//处理 gender 1: 男, 2: 女
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}else if("2".equals(gender)){
emp.setGender("女");
}
//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}else if("2".equals(job)){
emp.setJob("班主任");
}else if("3".equals(job)){
emp.setJob("就业指导");
}
});
//3. 响应数据
return Result.success(empList);
}
}
- 测试接口
- 访问页面: http://localhost:8080/emp.html
分层解耦
三层架构
如果我们把所有的代码都写在一个类中, 那么程序的可阅读和可维护性是极差的, 也不利于代码的复用
为了提高程序的维护效率, 设计的对象要符合单一职责原则
- Controller层: 控制层,负责接收前端请求, 并响应数据
- Service层: 业务逻辑层, 负责业务逻辑处理
- Dao层: 数据访问层, 负责数据访问操作, 包括数据的增,删,改,差
内聚耦合
内聚: 软件中各个功能模块内部的功能联系
耦合: 衡量软件中各个层/模块之间的依赖和关联的程度
软件设计原则: 高内聚, 低耦合
高耦合
- 通过三层架构拆分的代码, 提高了程序的内聚, 但是模块之间依然是耦合的
- 如果controller需要切换不同的server代码, 依然要改动controller
低耦合
- 为了实现模块之间的解耦合, 就出现了控制反转和依赖注入思想
- 所谓控制反转就是把对象交给容器管理, 对象的创建控制权由程序转移到容器, 简称IOC
- 所谓依赖注入, 就是程序运行时,所依赖的资源, 由容器提供, 简称DI
- 比如我们把A对象交给容器管理, 当程序中需要使用A类型的对象时, 容器自动创建A类型的对象
- 由IOC容器创建/管理的对象, 称之为bean对象
程序改造
通过Springboot提供的注解, 完成程序的解耦和
- 将Server层和Dao层的实现类,交给IOC容器管理,使用 @Component 注解
- 在Controller层 和 server层中, 注入运行时依赖的对象, 使用 @Autowired 注解
- 在程序运行时, IOC容器会自动创建使用到的对象, 并根据类型赋值给对应的变量
- 运行测试
IOC详解
介绍
对象的创建控制权由程序转移到外部容器, 这种思想称为控制反转, 简称IOC
为了降低程序的耦合度, SpringBoot提供了IOC和DI的相关注解
注解
作用: 将当前对象交给IOC容器管理, 成为IOC容器的Bean对象
- @Component注解是声明ben对象的基础注解, 其他注解都是衍生品
命名规则
声明Bean的时候可以通过value属性指定Bean的名字, 不指定默认为 类名首字母小写
@Repository(value = "daoA") 或 @Repository("daoA")
- 在springboot集成web开发中, 声明控制器Bean只能用@Controller注解
- 查看ben对象
组件扫描
@ComponentScan注解用于指定IOC容器的扫描范围, Bean对象想要生效. 就需要被组件扫描到
- 隐式配置: 启动类的 @SpringBootApplication注解 中,集成了@ComponentScan()注解
- 默认范围: 扫描启动类所在包及其子包
- 强烈建议: 把所有的bean对都放在启动类的包下, 避免麻烦
- 手动指定: 通过注解可以指定IOC容器扫描哪些包
@ComponentScan({ "dao", "com.itheima" })
@SpringBootApplication
public class SpringbootWebReqResApplication {
... ...
}
DI详解
容器为应用程序提供运行时所依赖的资源, 称为依赖注入
springboot提供了 @Autowired 注解, 用于依赖注入,
- 程序运行时, IOC容器会创建bean对象, 根据对象的类型, 赋值给变量, 供程序使用
类型注入
@Autowired注解, 默认是按照类型装配对象, 如果容器中存在多个同类型的Bean, 就会报错,
- 提升Bean的优先级
- 指定Bean的名称
- 按照名称装配
- 注解对比
- @Autowired是spring框架提供的注解, @Resource是JDK提供的注解
- @Autowired默认按照类型注入对象, @Resource默认按照名称注入对象