ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Java Web三层架构详解:Struts、Spring、Hibernate分工与实战

Java Web三层架构详解:Struts、Spring、Hibernate分工与实战

简介:这份文档面向Java Web入门与进阶开发者,系统讲解表示层、业务逻辑层与数据访问层的职责划分,并围绕Struts、Spring、Hibernate组成的SSH组合框架,说明如何借助Spring的IoC统一管理包括Struts Action在内的各类bean,从而搭建结构清晰、可复用且易维护的企业级Web应用。内容从框架技术概述切入,分别剖析Struts的MVC组件、Spring的IoC与依赖注入机制、Hibernate的对象关系映射原理,再延伸到SSH集成方案的设计与案例应用,兼顾概念梳理与落地思路。资源包共1个doc文件,约102KB,以文字讲解为主,适合作为架构认知与方案参考的轻量读物。目前已有2378人学习下载,读者可从中获得三层架构的职责边界认识、三大框架的协作方式以及分层解耦的实践思路,为后续项目开发与框架选型提供参考。

1. 到底什么是 Java Web 三层架构:从“后厨分工”说起

第一次听到“Java Web 三层架构”这个词,很多人的反应是:是不是要写三个项目?是不是要部署三台服务器?其实都不是。它说的是一种把代码按职责分开放的组织方式,就像一家餐厅不会让同一个人既点菜、又炒菜、又管仓库——前台负责接待客人,厨师负责做菜,库管负责备料,各干各的,出了问题也知道该找谁。Java Web 三层架构就是把一个 Web 应用拆成表示层、业务逻辑层、数据访问层三块,每块只操心自己的事。搞懂它的价值在于:你能看懂绝大多数企业级 Java 项目的目录结构,也能明白为什么老项目里总有一堆action、service、dao这样的包名。它不是为了炫技,而是为了让代码在多人协作时不至于乱成一锅粥。

2. 为什么要分层:三层架构到底解决了什么麻烦

2.1 不分层会怎样:一个“全写在 JSP 里”的反面例子

要理解三层架构为什么存在,先看看没有它的世界长什么样。早期不少 Java Web 项目把数据库连接、业务判断、页面输出全塞进一个 JSP 文件里:页面开头import一堆数据库驱动,中间写Connection conn = DriverManager.getConnection(...),下面再跟一段if (用户积分 > 100) { 打折 },最后用out.println把 HTML 拼出来。这种写法在 demo 阶段跑得挺欢,一旦需求变成“积分规则要改”“页面要换皮肤”“数据库要从 MySQL 换成别的”,麻烦就来了——改一个打折逻辑,得在几十个 JSP 里翻找同样的代码;换一个数据库,页面文件全得动。

打个比方,这就像把收银、炒菜、记账全写在一张纸上贴在墙上,谁都能改,改完谁也不知道别处还有没有同样的字。三层架构要做的,就是把这张纸撕成三本账:前台一本、后厨一本、仓库一本,每本只记自己的事,改规则只动对应的那本。

2.2 表示层、业务逻辑层、数据访问层各自管什么

三层架构的三层,职责边界其实很清晰。表示层负责和用户打交道:接收请求、展示数据,在经典的 Java Web 方案里通常由 Struts 这类 MVC 框架承担,JSP 页面只管显示,Action 只管把请求转给后面。业务逻辑层负责“这件事该怎么办”:比如用户注册时判断用户名是否重复、积分够不够、权限对不对,这一层由 Spring 这样的容器来管理对象和它们之间的依赖。数据访问层负责和数据库对话:把 Java 对象存进表里、把表里的行取出来变成对象,Hibernate 这类对象关系映射框架就是干这个的。

三层之间是上下依赖的关系:表示层调业务层,业务层调数据访问层,反过来不行。这条“单向依赖”的规矩,是三层架构能站住脚的根基。表示层不知道数据库长什么样,数据访问层也不关心页面怎么渲染,中间的业务层像一个翻译,把上层的请求翻译成下层能懂的操作。

2.3 分层的真实收益:可维护、可复用、可并行

分层带来的好处不是抽象的“架构优雅”,而是很具体的三件事。第一是可维护:业务规则变了只改业务层,页面改版只动表示层,数据库换方言只调数据访问层,改动范围被框住了。第二是可复用:同一个业务层的方法,既可以被网页调用,也可以被定时任务调用,不用为每个入口重写一遍逻辑。第三是可并行:前端的人按接口约定做页面,后端的人按接口约定写服务,两边不用等对方写完才能开工。

提示:分层的收益要在项目变大之后才明显。三五个人写的小工具硬套三层,反而多出一堆只做转发的空壳类,这是常见的过度设计,后面会专门讲怎么判断。

3. 三层架构里的三个角色:Struts、Spring、Hibernate 各站哪一班

3.1 Struts 站在表示层:把请求接住再转出去

Struts 是一个基于 MVC 模式的表示层框架,它的核心思路是把“表现逻辑”和“业务逻辑”拆开。一个请求进来,先由控制器(ActionServlet)接住,它读配置文件,决定把这个请求交给哪个 Action 处理;Action 干完活,再根据结果跳转到对应的 JSP 页面显示。JSP 里只放标记和展示,不写流程判断,也不碰数据库。

这套机制的价值在于:页面和流程被解耦了。改页面不用动流程配置,改流程也不用翻 JSP。Struts 还自带一组标签库,能把 JSP 里常见的循环、判断、表单绑定用标签表达出来,减少页面里的 Java 代码。用一句话概括它的位置:Struts 是三层架构里最上面那层的“调度员”,负责把用户的请求准确地送到该去的地方。

3.2 Spring 站在业务层:用 IoC 把对象关系交给容器管

Spring 在这一组合里承担业务逻辑层,它的看家本领是控制反转(IoC)和依赖注入(DI)。通俗地说,以前一个业务类要用到数据访问类,得自己new一个出来;用了 Spring 之后,业务类只管声明“我需要一个数据访问对象”,由容器在运行时把准备好的对象塞进去。对象之间的装配关系从代码里搬到了配置文件里。

这个转变的意义在于解耦。业务类不再关心数据访问类是怎么构造的、用的是哪个实现,换实现只改配置不改代码。Spring 的容器还能统一管理事务、连接池这些横切的服务组件,业务代码里不用到处写“开启事务、提交事务、回滚”的样板。原文里特别强调,Spring 的 IoC 容器可以管理应用里所有的 bean,包括 Struts 的 Action,这样三层之间的对象都由同一个容器统一装配,不会出现两套管理方式打架的情况。

3.3 Hibernate 站在数据访问层:让对象和表自动对上号

Hibernate 解决的是一个老矛盾:Java 是面向对象的,数据库是关系型的,两边结构对不上。它的做法是在 Java 对象和数据库表之间做一层自动映射——一个类对应一张表,一个属性对应一个字段,开发者写的是对象操作,Hibernate 在背后翻译成 SQL。

这样做的好处是程序员不用手写大量 JDBC 代码,也不用把 SQL 字符串散落在业务逻辑里。换数据库时,通常只需要改初始化配置文件里的方言设置,应用代码基本不动。Hibernate 还提供了自己的查询语言 HQL,写法接近面向对象,支持主流数据库。它在三层架构里的位置是最底下那层,只对业务层负责,不关心上面页面长什么样。

3.4 三者怎么串起来:一次请求的完整走位

把三个角色放进三层架构,一次请求的路径就清楚了:用户在 JSP 页面提交表单,Struts 的控制器接住请求,按配置找到对应的 Action;Action 调用 Spring 管理的业务组件;业务组件再调用数据访问对象;数据访问对象通过 Hibernate 和数据库交互,把结果一层层往回传,最后 Action 决定跳转到哪个页面展示。

这条链路里,每一层只认识它的下一层。Action 不认识 Hibernate,业务组件不认识 JSP,数据访问对象也不认识 Struts。正因为彼此不认识,任何一层内部换实现,其他层都受影响很小。这就是三层架构加 SSH 组合框架想要达到的效果:结构清晰、职责分明、改动可控。

4. 常见误区与易混概念:这些坑初学者最容易踩

4.1 三层架构不等于三个工程

最容易混的一点,是把“三层”理解成“三个独立的项目或三台服务器”。三层说的是逻辑分层,代码可以放在同一个工程里,用不同的包区分就行。比如一个用户管理模块,可以在公共包下建user包,里面再分controller、service、dao、model等子包。物理上部署在一起,逻辑上各司其职,这才是三层架构的常态。把它当成必须拆成三个工程,会带来不必要的部署和联调成本。

4.2 DAO 是接口还是实现类

这个坑原文作者自己都踩过,还专门写了“这个问题让我纠缠了很久”。DAO 的本意是数据访问对象,实践中更推荐先定义接口,再在单独的子包里放实现类。这样做的好处是业务层依赖接口而不是具体实现,Spring 的依赖注入才能发挥作用,将来换实现也不用改调用方。如果直接把 DAO 写成一个能操作的类,业务层就和具体实现绑死了,IoC 的意义也就丢了一半。区分方法很简单:看业务层引用的是接口类型还是实现类型,前者是对的。

4.3 VO 和 DTO 到底是不是一回事

VO(值对象)和 DTO(数据传输对象)经常被混用,原文作者也坦言“个人理解 VO 和 DTO 是类似的东西”。严格说,两者都用于在层之间传递数据,原则上只带公共字段、不带业务逻辑。区别更多在语境:VO 偏重展示层需要的数据形态,DTO 偏重跨层传输的数据载体。实际项目里很多人给它们加了 getter/setter 和一些辅助方法,这不算错,但要知道这已经偏离了“纯数据载体”的原意。初学阶段不必在这上面纠结太久,记住“它们都是用来传数据的轻量对象”就够用了。

4.4 分层越多越好吗

还有一种误区是觉得层分得越细越专业。三层架构本身已经够用,再往上叠“接口层”“门面层”“转换层”,如果每一层都只是把调用原样转发下去,那就是空壳。判断标准很实在:这一层有没有真实的职责?如果删掉它,代码只是少了一次转发、逻辑没有任何损失,那它就不该存在。分层是为了隔离变化,不是为了凑数。

4.5 用了框架就等于架构好

最后一个易混点:很多人以为上了 Struts、Spring、Hibernate 就等于有了好架构。框架只是工具,如果 Action 里照样写业务判断、业务层里照样拼 SQL,三层就只剩个空架子。架构好不好,看的是职责有没有真的分开,而不是看配置文件里出现了几个框架的名字。

5. 什么时候该用三层架构,什么时候别硬套

三层架构适合的场景其实有比较清晰的边界。当项目具备这几个特征时,分层是划算的:业务规则会反复变、参与开发的人不止一两个、系统要长期维护和扩展、数据来源可能更换。这类项目里,前期多写的那些接口和配置,会在后续每次改动时连本带利地还回来。原文举的那个问答式系统就是典型:模块多、要协作、要长期运营,三层加 SSH 的组合能把结构撑住。

反过来,以下几种情况硬套三层反而添乱。一是原型验证或一次性脚本,需求明天就可能推翻,分层带来的抽象成本收不回来。二是极小的内部工具,总共几百行代码、一个人维护,多出来的 Action、Service、DAO 三层空壳只会让阅读变累。三是团队对框架本身还不熟,配置还没跑通就先搭三层,容易把“配置问题”误判成“架构问题”,排查起来两头堵。

判断维度适合分层不建议硬套
业务规则变化频率经常变,需要隔离基本不变或一次性
参与人数多人协作单人短周期
维护周期长期运营用完即弃
数据来源可能更换固定不变

还有一点值得单独说:三层架构的“层”是逻辑概念,不是物理部署单位。同一个工程里用包结构把controller、service、dao、model分开,就已经是在实践三层架构了,不必为了“标准”去拆成多个工程。原文给出的目录方案就是这种思路——按模块建包,模块内再按职责分子包,公共包统一前缀,找代码时顺着包名就能定位到层。

我自己的习惯是,每次新建一个模块,先把三个包的骨架摆出来,再往里填东西;如果写着写着发现某个类在三层之间来回横跳,就停下来想想到底该归哪一层。这个动作花不了几分钟,但能省掉后面很多“这段逻辑到底写在哪”的翻车时刻。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进