从混乱到秩序:Java Web 分层学习的迭代之路

一、总:为什么分层不能一蹴而就?

很多初学者学 Java Web 时,一上来就被灌输了“标准三层架构”:Controller、Service、DAO、Entity……然后照着模板写代码,却始终不理解为什么要多写这么多类和接口。结果往往是:分层流于形式,代码越改越乱,甚至比不分层还难维护。

真正的分层不是设计出来的,而是生长出来的。本文提出一种“迭代法学习路径”:
先写一个什么都干的“上帝 Controller” → 随着业务复杂,自然抽离 Service 层 → 再进一步抽离持久层 → 最后引入 Spring Boot 解决工程化困境。
整个过程遵循奥卡姆剃刀原则——如非必要,勿增实体。让学习者在每一次“痛”中理解分层的价值,最终真正掌握分层架构的灵魂。


二、分:四步迭代,从混沌走向秩序

第一阶段:上帝 Controller —— 允许一切从简单开始

目标:快速搭建一个可运行的 Web 应用,所有业务逻辑、数据访问、页面跳转全部写在一个 Controller 里。

示例场景:一个简单的图书管理功能,只有“查看所有图书”和“添加新书”。

@WebServlet("/book")
public class GodController extends HttpServlet {
    // 假设内存里有个 List<Book> 模拟数据库
    private List<Book> books = new ArrayList<>();

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String op = req.getParameter("op");
        if("list".equals(op)) {
            req.setAttribute("books", books);
            req.getRequestDispatcher("/bookList.jsp").forward(req, resp);
        }
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        String name = req.getParameter("name");
        books.add(new Book(name));
        resp.sendRedirect("book?op=list");
    }
}

这个阶段学到什么?

· 理解 HTTP 请求流转、页面跳转、参数获取。
· 感受到原始的快乐:代码集中,改一处逻辑不用翻来翻去。
· 也为未来的“痛”埋下伏笔:当业务膨胀时,这个类会变得臃肿难读,测试困难,逻辑复用为零。

关键原则:在不需要复杂分层时强行分层,只会增加无意义的模板代码。先跑起来,再谈架构。


第二阶段:业务扩张 → Service 层自然涌现

触发条件:系统开始增加“用户”、“管理员”等身份,出现多处共享的业务规则。例如:

· 添加图书时,需要验证书名不能为空、不能重复。
· 删除图书时,需要检查当前用户是否有权限。
· 这些验证规则既在 Web 端用到,也可能在定时任务、REST 接口中用到。

此时感受到的痛:同一个验证逻辑写在多个地方,改一处要改 N 处;GodController 已经超过 500 行,没人愿意维护。

解决方案:把业务规则抽离成 Service 层。Controller 只负责接收请求、调用 Service、返回视图。

// 业务层
public class BookService {
    public void addBook(String bookName) {
        // 复杂的验证逻辑、业务计算都在这里
        if(StringUtils.isBlank(bookName)) throw new IllegalArgumentException();
        // 调用持久层(暂时还没有,先模拟)
    }
}

// Controller 瘦身
@WebServlet("/book")
public class BookController {
    private BookService bookService = new BookService();
    
    protected void doPost(...) {
        bookService.addBook(req.getParameter("name"));
        resp.sendRedirect(...);
    }
}

这个阶段学到什么?

· Service 层隔离了业务逻辑和 Web 协议:可以在不修改 Controller 的情况下,单独测试 Service。
· 当需要给移动端提供 API 时,只要再写一个 RestBookController,复用同一个 BookService 即可。
· 真正理解:分层不是“多写一个类”,而是职责分离。

关键原则:非必要不增加实体。只有当 Controller 里的逻辑已经无法清晰维护、无法复用时,才抽出 Service。这个“痛”就是最好的老师。


第三阶段:数据多样化 → 持久层自然抽离

触发条件:系统需要从内存数据库换成 MySQL,或者需要支持多种数据库(如测试用 H2,生产用 MySQL)。此外,每个 Service 方法里都重复写着 JDBC 代码:Connection、PreparedStatement、ResultSet、异常处理……

此时感受到的痛:

· 换数据库要改几十个 Service,改到崩溃。
· 同样的 SQL 拼接散落在各处,极易出错。
· 数据访问的细节污染了业务逻辑,一改数据库表结构,Service 也得跟着改。

解决方案:抽离 DAO(Data Access Object)+ Entity 层。

· Entity:与数据库表一一对应的 Java 对象,是数据的运行时载体。
· DAO:封装所有 CRUD 操作,让 Service 完全不感知底层是 MySQL 还是文件。

// Entity
public class Book {
    private Long id;
    private String name;
    // getter/setter
}

// DAO 接口
public interface BookDao {
    List<Book> findAll();
    void save(Book book);
}

// DAO 实现(JDBC 模板)
public class BookDaoJdbcImpl implements BookDao {
    public List<Book> findAll() {
        // 具体的 JDBC 代码
    }
}

// Service 只依赖接口
public class BookService {
    private BookDao bookDao; // 通过构造注入
    public void addBook(String name) {
        Book book = new Book();
        book.setName(name);
        bookDao.save(book);
    }
}

这个阶段学到什么?

· 持久层隔离了数据存储的细节:要换数据库,只需换一个 DAO 实现,Service 一行不改。
· Entity 让数据结构清晰:再也不需要用 Map<String, Object> 到处传数据。
· 之前所谓的“三层架构”现在才真正完整,而且每一层都是在无法忍受混乱时自然涌现出来的,不是凭空拍脑门。

关键原则:勿增实体并不意味着永远不加类,而是在真正需要的时候才加。持久层是最后出现的,也是最稳定的一层。


第四阶段:工程化困境 → Spring Boot 全面接管

触发条件:项目已经成长为一个小型产品:有十几个 Service、二十几个 DAO、多个拦截器(权限、日志)、需要事务管理、需要连接池、需要整合第三方服务(如 Redis、消息队列)。此时开发人员每天都在写重复的配置代码:

· 每个 Service 都要手动 new 依赖,想要换一个 DAO 实现得改好几个文件。
· 每个方法都要手动开启/提交/回滚事务,漏掉一个就出 bug。
· 写一个拦截器要配 web.xml,加一个过滤器又要配一遍。
· 打包部署要配置 web.xml、配置数据源、部署到 Tomcat 并手动启动……
· 用 Maven/Gradle 管理了 JAR 包版本,但各 JAR 包之间的兼容性问题、传递依赖冲突让人焦头烂额。

此时感受到的痛:

· 重复的样板配置已经远超真正的业务代码。
· 项目启动慢、测试困难、多人协作时每人电脑环境不一致。
· 开始怀念第一阶段“上帝 Controller”的简单——但也知道,那种简单无法支撑现在的规模。

解决方案:引入 Spring Boot。它不是要推翻之前的分层成果,而是接管基础设施,让开发者专注于业务。

Spring Boot 解决了哪些“痛”?

原生开发的痛点 Spring Boot 的解法
手动管理对象依赖(new、构造器传参) IoC 容器:用 @Autowired 自动注入,只需定义接口,容器帮你组装
到处写 try-catch-finally 管理事务 声明式事务:@Transactional 一行搞定,出现异常自动回滚
配置 web.xml、数据源、拦截器很繁琐 自动配置:引入 spring-boot-starter-web 就自动配好 Tomcat、DispatcherServlet
整合第三方库(Redis、MQ)需要大量配置 Starter 依赖:spring-boot-starter-data-redis 开箱即用,极少配置
打包部署麻烦,每台机器要装 Tomcat 内嵌 Servlet 容器:java -jar 直接运行,环境一致性有保障
依赖版本冲突、传递依赖混乱 Spring Boot Parent + Starter:精选的版本组合,减少 95% 的版本冲突
单元测试要启动整个 Web 容器 @SpringBootTest + 内嵌 Mock 环境,测试快如闪电

迭代改造示例:从原生到 Spring Boot

改造前(原生三层) 需要手动组装依赖:

// 手动创建依赖链
BookDao dao = new BookDaoJdbcImpl();
BookService service = new BookService(dao);
BookController controller = new BookController(service);

改造后(Spring Boot) 只需声明依赖关系:

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;  // 容器自动注入具体的 DAO 实现
}

@RestController
public class BookController {
    @Autowired
    private BookService bookService;
    
    @PostMapping("/book")
    public Result addBook(@RequestParam String name) {
        bookService.addBook(name);
        return Result.success();
    }
}

至于 BookDao 的具体实现是 Jdbc 还是 MyBatis,通过配置文件 application.properties 一行就能切换,Service 代码零修改。

这个阶段学到什么?

· 框架不是来取代分层的,而是让开发者更专注于业务分层。Service、DAO、Entity 概念在 Spring Boot 中依然完全适用,只是不用再写那些啰嗦的创建和管理代码。
· 自动配置不是魔法:依然需要理解底层的 Tomcat、JDBC、事务原理,但 Spring Boot 帮助避免了重复劳动。
· “约定优于配置” 是奥卡姆剃刀在框架层面的体现——在大多数情况下,框架的默认约定已经够用,不必为每个细节写配置。

关键原则:非必要不增加配置。Spring Boot 的理念与“如非必要,勿增实体”高度一致——只有当默认约定不满足时,才写额外配置。不会再看到一个几百行的 web.xml 或 spring.xml。


三、总:迭代分层的智慧 —— 从上帝到 Spring Boot 的完整路径

回顾整个学习路径:

  1. 上帝 Controller —— 理解 Web 基础,允许一切简单。
  2. Service 层涌现 —— 解决逻辑复用与维护难题。
  3. 持久层抽离 —— 实现数据库解耦,消除重复数据代码。
  4. Spring Boot 引入 —— 解决工程化配置、依赖管理、环境一致性等规模化痛苦。

可以发现:每一步都是在无法忍受当前阶段的痛苦后,自然迈入下一阶段。这正是奥卡姆剃刀原则的完美实践——永远不要在不需要的时候增加复杂性,也永远不要在需要的时候拒绝必要的抽象。

很多教程让初学者直接从 Spring Boot + 三层架构开始,导致学习者只会机械地复制注解,却说不清为什么需要 Service、为什么要有接口。而这条“迭代之路”,每一次抽象都是由痛驱动,会对每一层的价值刻骨铭心。

最后,送给你三句心法:

· 所有的架构演进,都始于对当前痛苦的诚实面对。
· 框架是用来解放你的,不是用来束缚你的。 学会的分层思想,在 Spring Boot 里依然闪闪发光。
· 真正的专家,不是知道所有设计模式的人,而是知道什么时候该用、什么时候该保持简单的人。

从今天起,带着这种“迭代思维”去学习任何新技术:先手工作业,感受痛点;再引入工具,解决问题。这样的成长路径,会比任何人都快。


本文使用了AI辅助生成,欢迎大家一起讨论。

posted @ 2026-05-31 00:19  嘉君  阅读(20)  评论(0)    收藏  举报