当前位置: 首页 > news >正文

B/S架构,三层架构,跟分层解耦

B/S架构:

浏览器 → Web 服务器 → 数据库(即浏览器通过web服务器与数据库交互)

  • B 端:浏览器
  • S 端
    • Tomcat
    • Controller
    • Service
    • Dao/Mapper
    • 数据库

TomCate是标准的标准 Java Web 服务器

用于

  • 监听端口(8080)
  • 接收 HTTP 请求
  • 解析请求
  • 把请求交给你的项目(Servlet / SpringMVC)
  • 返回 HTTP 响应
浏览器(B) ↓ HTTP 请求(底层走 Socket 连接) ┌───────────────────────────┐ │ sereverSocket │ ← 网络通信通道(TCP 连接),接受请求建立通道,创建socket读 └───────────────────────────┘ 取数据 ↓ ┌─────────────────────┐ │ Tomcat服务器 │ ←—— 服务器容器,不属于三层任何一层,监听socket,接受请求 └─────────────────────┘ ↓ 把请求交给 Controller ┌─────────────────────┐ │ Controller │ ←—— 表现层(三层之一) │ Service │ ←—— 业务层 │ Dao/Mapper │ ←—— 数据访问层 └─────────────────────┘ ↓ 数据库 返回是原路返回

三层架构

  1. 表现层(Presentation Layer)——Controller
  2. 业务逻辑层(Business Logic Layer)——Service
  3. 数据访问层(Data Access Layer)——Dao / Mapper

另外还有配套的实体类层(Entity/Model/Pojo),不属于三层,但必须一起用。

表现层(Controller)

  • 接收前端(浏览器 / Postman)发来的求
  • 参数校验、简单格式处理
  • 调用 Service 层完成业务
  • 把结果封装成 JSON 返回给前端
  • 不写业务逻辑,不直接操作数据库

业务逻辑层(Service)

  • 真正写业务逻辑的地方
  • 事务控制、权限判断、数据计算、流程编排
  • 调用 Dao/Mapper 层操作数据库
  • 一个 Service 可以调用多个 Dao,也可以调用其他 Service

数据访问层(Dao / Mapper)

  • 只负责和数据库交互
  • 增删改查(CRUD)
  • 写 SQL 或使用 MyBatis/MyBatis-Plus/JPA
  • 不处理业务逻辑,只做数据存取

调用关系

只能从上往下调用,不能反向!

前端 →Controller → Service → Dao/Mapper→ 数据库

标准的包结构

com.xxx ├── controller │ └── UserController.java // 控制器 ├── service │ ├── UserService.java // 接口! │ └── impl │ └── UserServiceImpl.java // 实现类 ├── mapper │ └── UserMapper.java └── entity └── User.java

分层的作用利于团队协作,方便更改,解耦....

分层解耦

分层

把代码按职责切成三层:

  • Controller(表现层):接收请求、返回结果
  • Service(业务层):写逻辑、判断、计算
  • Dao/Mapper(数据层):操作数据库

各干各的

耦合

就是就是类和类绑得太紧

public class UserController { private UserService userService = new UserServiceImpl(); }

Controller直接 new Service→ Controller 死死依赖 UserServiceImpl→ 这就叫高耦合

耦合的坏处:

  • 换实现类必须改源码
  • 一处改,处处动
  • 代码难维护、难测试

解耦

就是解除这种硬绑定,让类之间只依赖 “接口”,不依赖具体类。

解耦的操作依赖于IOC + DI + Bean 这套 Spring 机制

IOC + DI + Bean
1. IOC 控制反转 —— 解耦的思想

IOC 前:

  • 对象是你自己 new,控制权在自己写的代码里

IOC 之后:

  • 对象由Spring 容器创建,控制权从代码 → 反转给容器,变成被动接
2.Bean = Spring 容器创建并管理的对象(被IOC 注解标记类后就会被spring创建)
  • Controller、Service、Dao 这些类的实例,是 由Spring 创建的,放在 Spring 容器里统一管理
3. DI 依赖注入 —— IOC 的具体动作

DI 做的事:

  • Spring 容器里已经有一堆 Bean 了,只用在类里写@Autowired,Spring 自动把对应的 Bean 赋值给你的变量,全程不用 new,不用管对象从哪来

这就叫依赖注入

完整的讲就是

先用 IOC 相关注解(@RestController、@Service、@Repository、@Component)标记类 → 告诉 Spring:这个类要创建对象,放进容器里。 Spring 项目启动时,自动扫描这些类,自己 new 出对象,放进 Spring 容器。 容器里的这些对象,就叫 Bean。 然后用 DI 注解(@Autowired)告诉 Spring:把容器里对应的 Bean 拿过来,赋值给我这个变量。

IOC相关注解

注解归属层 / 用途使用位置核心作用
@RestController表现层(Controller)控制器类标记为 Web 接口,返回 JSON,前后端分离专用
@Controller表现层(Controller)控制器类标记为控制器,返回页面 / 视图,传统项目用
@Service业务层(Service)业务实现类标记业务逻辑类,处理规则、计算、判断
@Repository数据访问层(Dao/Mapper)数据操作类标记数据库操作类,自动包装数据库异常
@Component通用组件工具类、公共类不属于三层的通用类,交给 Spring 管理
@Configuration配置类配置类用于编写配置、注册第三方 Bean

现在

//Controller @RestController // 标记:交给容器管理 @RequestMapping("/user") public class UserController { // 连接方式:注入 Service @Autowired private UserService userService;//接口变量,用来装它实现类对象的变量 @GetMapping("/user/{id}") public User get(@PathVariable Long id) { // 调用接口方法 return userService.findById(id); } // Service 接口 public interface UserService { User getById(Long id); } // Service 实现 @Service public class UserServiceImpl implements UserService { // 注入 Mapper @Autowired private UserMapper userMapper; @Override public User findById(Long id) { // 1. 查数据库 User user = userMapper.selectById(id); // 2. 业务判断 if (user == null) { throw new RuntimeException("用户不存在"); } // 3. 返回给 Controller return user; } } @Mapper//Mapper(接口)代替了 @Repository 因为MyBatis会自动生成实现类(代理对象),并交给Spring 生成Bean public interface UserMapper { // 写 SQL,操作数据库 // MyBatis 帮你写的 实现类 // (你看不见,但是真的存在) public class **UserMapperImpl** implements UserMapper { // 真正干活的代码 (JDBC连接数据库) } }

以前

public class UserController { // 1. 声明一个变量 private UserService userService; public UserController() { // 2. 自己 new 对象,赋值给变量 userService = new UserServiceImpl(); } }
http://www.jsqmd.com/news/636075/

相关文章:

  • 再次革新 .NET 的构建和发布方式(一)票
  • 技术日报|hermes-agent再揽7454星两日合计近1.4万,微软markitdown突破10万星
  • Claude Code 上下文管理机制
  • TP4086集成 MCU 开关充电管理 SOC
  • 面向教育 Agent 的 Harness 学习进度持久化
  • 别再暴力搜索了!用贪心+回溯优化‘数字组合’问题,C++代码效率提升10倍
  • 从家居电路模拟程序看Java设计模式:如何用策略、工厂模式重构你的大作业代码
  • 如何将小爱音箱打造成智能音乐中心:Xiaomusic完全指南
  • Linux(十一)fork实例练习、文件操作示例及相关面试题目分享
  • 手柄映射终极指南:如何让任何游戏都支持你的游戏手柄
  • Spring-Boot-缓存实战-@Cacheable-这10个坑
  • 用100行Python代码理解AI Agent的本质
  • 1、说说你对 TypeScript 的理解?与 JavaScript 的区别?
  • Spring Boot + MyBatis-Plus 多租户实战:从数据隔离到权限控制的完整方案
  • 【YOLOv11】010、YOLOv11训练流程详解:从数据加载到模型保存的完整步骤
  • AI大模型背后的关键单位,你真的了解它吗?
  • cpp算法编程中可能用到的几何知识
  • 2026年青岛发电车租赁公司推荐榜:市南区/市北区/黄岛区/崂山区/李沧区/城阳区/即墨区/胶州市/平度市发电车租赁公司选择指南 - 海棠依旧大
  • 模型、Harness与记忆如何帮助Agent持续学习(Continual Learning)?
  • 高效稳定LDO芯片选型指南:从原理到实战应用
  • VutronMusic:你的跨平台音乐播放器终极解决方案
  • 2026年大模型从入门到精通:AI风口必学,高薪技能速成!错过等不起!
  • Java: File
  • 前端创新技术探索
  • LeetCode Hot100 - 4. 移动零(Java 题解)
  • 009、Python流程控制:条件判断(if/elif/else)
  • 函数用法记录——MATLAB符号计算
  • uniapp Uview框架中u-search组件实现动态搜索与数据过滤
  • Tiktokenizer高性能架构设计:深入解析Token可视化引擎的实现原理
  • WPF DataContext实战:三种绑定方式深度解析