day8 SpringBootWeb入门、HTTP协议与分层解耦

day8

计划

  1. SpringBootWeb入门
  2. HTTP协议学习
  3. SpringBootWeb案例
  4. 分层解耦与IOC/DI

笔记

1.SpringBootWeb入门

Spring家族简介
  • SpringFramework:Spring家族最基础、最核心的框架,提供依赖注入、事务管理、Web开发支持等功能
  • SpringBoot:简化配置、快速开发,是企业开发的主流方式
  • Spring全家桶:Spring家族旗下一系列技术,解决不同领域的问题
起步依赖
  • spring-boot-starter-web:包含Web应用开发所需的常见依赖,内嵌Tomcat服务器
  • spring-boot-starter-test:包含单元测试所需的常见依赖
  • 起步依赖本质:预定义的依赖项集合,提供特定场景下开发所需的所有库和配置
入门程序
1
2
3
4
5
6
7
8
9
@RestController  //标识当前类是一个请求处理类,包含@Controller和@ResponseBody
public class HelloController {

@RequestMapping("/hello") //标识请求路径
public String hello(String name){
System.out.println("HelloController ... hello: " + name);
return "Hello " + name;
}
}
  • @RestController:组合注解,包含@Controller@ResponseBody
  • @RequestMapping:映射请求路径
  • 启动类:带有@SpringBootApplication注解的类,运行main方法启动内嵌Tomcat

2.HTTP协议

HTTP概述
  • HTTP:超文本传输协议,规定浏览器与服务器之间数据传输的规则
  • 特点
    • 基于TCP协议:面向连接,安全
    • 基于请求-响应模型:一次请求对应一次响应
    • 无状态协议:对数据没有记忆能力,每次请求-响应独立
HTTP请求协议
  1. 请求行:请求方式、资源路径、协议/版本
  2. 请求头:key: value形式,包含浏览器信息等
  3. 请求体:存储请求参数(POST请求使用)
请求头 含义
Host 请求的主机名
User-Agent 浏览器版本
Accept 浏览器能接收的资源类型
Content-Type 请求主体的数据类型
GET vs POST请求
区别 GET请求 POST请求
请求参数位置 请求行中 请求体中
请求参数长度 有限制 无限制
安全性 低(暴露在地址栏) 相对高
获取请求数据
1
2
3
4
5
6
7
8
9
@RequestMapping("/request")
public String request(HttpServletRequest request){
String name = request.getParameter("name"); //获取请求参数
String uri = request.getRequestURI(); //获取请求URI
String url = request.getRequestURL().toString(); //获取请求URL
String method = request.getMethod(); //获取请求方式
String header = request.getHeader("User-Agent"); //获取请求头
return "request success";
}
HTTP响应协议
  1. 响应行:协议/版本、响应状态码、状态码描述
  2. 响应头:key: value形式
  3. 响应体:存储响应数据
响应状态码
分类 说明
1xx 临时状态码,表示请求已接受
2xx 成功,表示请求已被成功接收处理
3xx 重定向,让客户端再发起请求
4xx 客户端错误
5xx 服务器端错误

常用状态码

  • 200 OK:请求成功
  • 404 Not Found:请求资源不存在
  • 500 Internal Server Error:服务器发生错误
设置响应数据
1
2
3
4
5
6
7
8
9
10
11
12
13
//方式1:使用HttpServletResponse
@RequestMapping("/response")
public void response(HttpServletResponse response) throws IOException {
response.setStatus(404); //设置响应状态码
response.setHeader("name", "zhang"); //设置响应头
response.getWriter().write("<h1>hello springboot</h1>"); //设置响应体
}

//方式2:使用ResponseEntity
@RequestMapping("/response01")
public ResponseEntity<String> response01() {
return ResponseEntity.status(200).header("name", "li").body("hello springboot");
}

3.SpringBootWeb案例

需求说明

基于SpringBoot开发Web程序,完成用户列表的渲染展示:

  1. 前端页面发送ajax请求(/list)
  2. 服务端加载user.txt文件数据
  3. 返回JSON格式数据给前端
  4. 前端渲染展示在表格中
代码实现

1). 实体类User

1
2
3
4
5
6
7
8
9
10
11
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
private LocalDateTime updateTime;
}

2). Controller层

1
2
3
4
5
6
7
8
@RestController
public class UserController {
@RequestMapping("/list")
public List<User> list() {
//读取文件、解析数据、响应JSON
return userList;
}
}

@ResponseBody注解

  • 将方法返回值直接响应给浏览器
  • 如果返回值是实体对象/集合,会自动转换为JSON格式
  • @RestController包含@ResponseBody,无需单独使用

4.三层架构

架构介绍
  • Controller(控制层):接收前端请求,处理请求,响应数据
  • Service(业务逻辑层):处理具体业务逻辑
  • Dao(数据访问层):负责数据访问操作(增删改查)

执行流程:前端请求 → Controller → Service → Dao → 文件/数据库

优势

  1. 复用性强
  2. 便于维护
  3. 利于扩展
代码拆分

1). Controller层

1
2
3
4
5
6
7
8
9
10
@RestController
public class UserController {
private UserService userService = new UserServiceImpl();

@RequestMapping("/list")
public List<User> list() {
List<User> userList = userService.findAll();
return userList;
}
}

2). Service层(接口+实现类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//接口
public interface UserService {
List<User> findAll();
}

//实现类
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();

@Override
public List<User> findAll() {
List<String> lines = userDao.findAll();
//解析数据为User对象
return userList;
}
}

3). Dao层(接口+实现类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//接口
public interface UserDao {
List<String> findAll();
}

//实现类
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
List<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}

5.IOC&DI(控制反转与依赖注入)

问题分析

直接new对象会导致层与层之间耦合,难以维护和扩展。

软件设计原则:高内聚低耦合

解耦思路
  1. 将对象交给容器管理(IOC容器)
  2. 程序运行时从容器中获取依赖对象(依赖注入)

核心概念

  • IOC(控制反转):对象的创建控制权由程序自身转移到外部容器
  • DI(依赖注入):容器为应用程序提供运行时所依赖的资源
  • Bean对象:IOC容器中创建、管理的对象
Bean的声明

使用@Component及其衍生注解将类交给IOC容器管理:

注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时使用
@Controller @Component的衍生注解 控制层类
@Service @Component的衍生注解 业务层类
@Repository @Component的衍生注解 数据访问层类
组件扫描
  • @ComponentScan:扫描指定包及其子包下的bean
  • @SpringBootApplication包含@ComponentScan,默认扫描启动类所在包及其子包
依赖注入(@Autowired)

方式1:属性注入

1
2
3
4
5
@RestController
public class UserController {
@Autowired
private UserService userService;
}
  • 优点:代码简洁
  • 缺点:隐藏依赖关系,可能破坏封装性

方式2:构造函数注入(推荐)

1
2
3
4
5
6
7
8
9
@RestController
public class UserController {
private final UserService userService;

@Autowired //只有一个构造函数时可省略
public UserController(UserService userService) {
this.userService = userService;
}
}
  • 优点:依赖关系清晰,安全性高

方式3:setter注入

1
2
3
4
5
6
7
8
9
@RestController
public class UserController {
private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
多个相同类型Bean的解决方案

方案1:@Primary

1
2
3
4
@Primary
@Service
public class UserServiceImpl implements UserService {
}

方案2:@Qualifier

1
2
3
4
5
6
@RestController
public class UserController {
@Qualifier("userServiceImpl")
@Autowired
private UserService userService;
}

方案3:@Resource

1
2
3
4
5
@RestController
public class UserController {
@Resource(name = "userServiceImpl")
private UserService userService;
}
@Autowired与@Resource的区别
区别 @Autowired @Resource
所属框架 Spring框架 JDK提供
默认注入方式 按类型注入 按名称注入

6.完整代码示例

最终项目结构
1
2
3
4
5
6
7
8
9
10
11
12
springboot-web-04/
├── src/main/java/com/zhang/
│ ├── controller/UserController.java #控制层
│ ├── service/UserService.java #业务层接口
│ ├── service/impl/UserServiceImpl.java #业务层实现
│ ├── dao/UserDao.java #数据访问层接口
│ ├── dao/impl/UserDaoImpl.java #数据访问层实现
│ ├── pojo/User.java #实体类
│ └── SpringbootWeb04Application.java #启动类
└── src/main/resources/
├── user.txt #数据文件
└── application.properties #配置文件
UserController(最终版本)
1
2
3
4
5
6
7
8
9
10
11
@RestController
public class UserController {
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;

@RequestMapping("/list")
public List<User> list() {
return userService.findAll();
}
}
UserServiceImpl(最终版本)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;

@Override
public List<User> findAll() {
List<String> lines = userDao.findAll();
return lines.stream().map(line -> {
String[] parts = line.split(",");
return new User(
Integer.parseInt(parts[0]),
parts[1], parts[2], parts[3],
Integer.parseInt(parts[4]),
LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
);
}).toList();
}
}
UserDaoImpl(最终版本)
1
2
3
4
5
6
7
8
@Repository
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
return IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
}
}

7.核心知识点总结

知识点 说明
@RestController 组合注解,包含@Controller和@ResponseBody
@RequestMapping 映射请求路径
@ResponseBody 将返回值转为JSON响应给浏览器
@Service 声明业务层Bean
@Repository 声明数据访问层Bean
@Autowired 自动装配,按类型注入依赖
@Qualifier 按名称选择注入的Bean
@Primary 指定默认主Bean
@Resource JDK提供,按名称注入
IOC 控制反转,对象由容器管理
DI 依赖注入,容器提供依赖对象
三层架构 Controller→Service→Dao