本节摘要:动作元素是以 jsp 为前缀的标签家族,在运行期执行包含、转发与对象绑定:jsp:include 按请求动态并入片段输出,jsp:forward 把请求转手并可携带参数,jsp:useBean 三剑客完成对象的查找、绑定与属性读写。它们是"用标签写 Java"的过渡形态,也是视图去脚本化之前最早的声明式尝试。本站收拢语法清点的最后一堆物料,并交代这批元素在后续改造中的归宿。
上一节的静态包含陷阱已经把答案剧透了一半:内容随请求变化的片段,得用运行期的包含。动作元素就是运行期的动词。先看最常用的动态包含:
<%-- 顶部横幅:按登录态动态生成 --%> <jsp:include page="/common/banner.jsp"> <jsp:param name="zone" value="booklist"/> </jsp:include>
执行到这行时,容器把 banner 页面跑一遍,把它的输出追加进当前响应流,然后回到本页继续往下执行。主页面与片段各自独立翻译、独立编译,只共享同一次请求。与指令版包含的差异,一张表说清:
| 对比项 | include 指令(静态) | jsp:include 动作(动态) |
|---|---|---|
| 执行时机 | 翻译期,编译前粘贴 | 请求期,每次访问都执行 |
| 产物形态 | 融进主页面一个类 | 片段是独立页面,输出并流 |
| 片段更新 | 不触发主页面重编译 | 片段自会重译,主页面无感 |
| 片段内变量 | 与主页面互通 | 完全隔离,只能靠 request 传 |
| 适用场景 | 固定不变的结构件 | 随请求变化的活内容 |
jsp:forward 则是 2.3 节讲过的那条转发路线的标签写法:终止当前页面执行、把请求连参数一起转手。配合 jsp:param 可以在转手时附加新参数——老页面里常见的"处理页把错误码转发给统一错误页"就是这个组合。写法与陷阱(跳转后立刻返回、转发环)都在 2.3 节讲透了,此处不赘述。
三剑客是动作元素里最成体系的一组:jsp:useBean 负责找对象或造对象,jsp:setProperty 负责写属性,jsp:getProperty 负责读属性。典型用法是表单提交后的数据回收:
<jsp:useBean id="order" class="com.qingwu.bean.Order" scope="request"/> <%-- 按名自动绑定:请求参数 orderName 自动喂给同名属性 --%> <jsp:setProperty name="order" property="*"/> <p>收货人:<jsp:getProperty name="order" property="orderName"/></p>
useBean 的语义是"查找即创建":先去指定作用域找 id 对应的对象,找到就复用,找不到才实例化并存进去。这个决策过程值得单独一张图:
setProperty 的绝活是按名绑定:属性名写成星号时,容器把请求参数与 Bean 属性同名配对,逐个调用写入方法。表单有二十个字段,页面上一行绑定代码就够——这在当年是革命性的省键盘功能。它的前提也藏在机制里:参数名与属性名必须严格对应,且 Bean 要有无参构造与配套的读写方法。老代码绑定"偶发丢字段",第一件事就是核对两边名字是否一致。
上一节的页脚工单还有个姊妹案,正好把本节知识用全。
背景:运营希望首页横幅按登录状态变化:未登录显示促销,已登录显示会员折扣。当前横幅是静态包含的纯 HTML 片段,两百多个页面共用,无法感知登录态。
操作:把横幅片段改写成能读会话的迷你页面,把各页面的 include 指令逐个换成 jsp:include 动作, zone 参数照旧传递。改造按流量从低到高分批推进,每批观察一次容器日志。
结果:横幅随登录态正确变化;由于片段独立编译,这次改造只动了片段本身与各页面的一行引用,风险面很小。
解读:这个案例是"静态件用指令、活内容用动作"口诀的完整落地。也顺带看清了动作元素的历史定位:它是当年团队"不敢在页面写 Java 又想要动态行为"之间的折中——用标签把 Java 调用包起来。今天你自然可以用 EL 读取登录态(第 5 章),但理解这层折中,你才能读懂老代码里大量 useBean 写法的意图,而不是急着全盘否定。
变式:如果片段逻辑继续膨胀(条件分支多、要查数据),动态包含也会力不从心——它本质还是"页面套页面"。升级路径是先把逻辑抽到控制器,视图只留展示,这正是下一章 MVC 改造的标准动作。判断尺度:片段里的逻辑超过三五行的分支判断,就该外迁了。
💡 关键直觉:动作元素是语法体系里的"过渡化石"——它宣告了页面需要声明式表达,又没完全摆脱 Java 语义。读老站时把它当路标:满页动作元素的页面,说明当年已经走在去脚本化的路上,改造成本往往低于满页脚本片段的页面。
清点的最后要划清动作元素能做什么、不能做什么。能做:请求期的资源包含与转发、参数附加、对象的查找创建与属性绑定——它们覆盖了"结构组合与数据绑定"两大类页面需求。不能做:任意流程控制(没有版本的动作元素能写 while)、复杂数据加工(属性绑定之外的表达式能力归 EL)、事务与资源管理(那是第 4 章服务层的地盘)。边界清晰意味着判断简单:页面需求落在"组合与绑定"之内,动作元素够用;一旦越界,正路是往后端搬逻辑,而不是变着花样在视图里造流程。青梧书肆改造时定过一条口径:动作元素存量原样保留,新需求一律走 EL 加标签、逻辑归服务层的正路——存量不折腾,增量不上船,两层夹击之下旧写法自然退出历史舞台。
清点动作元素时有个现象值得留意:页面里 useBean 与 setProperty 的组合,本质上在做"数据装配",而这恰是第 4 章控制器该干的活。两者是接力关系——动作元素是页面自己动手装配数据的旧形态,MVC 改造后装配责任移交控制器,动作元素便只剩展示端的用武之地。青梧书肆的改造把这条接力做成了明规则:改造过的页面禁用 useBean,数据一律从托盘取;未改造的页面保持原样,等第 4 章的手术名单排到它。明规则的好处是页面一眼可辨新旧——看见 useBean 就知道这页还没轮到,看托盘取值就知道已经改完。语义清晰的新旧分界,在几十个页面的工程里价值巨大:它让"改到哪了"不再是口头记忆,而是代码里的事实。
到此,页面的三大语法家族——脚本元素、指令、动作元素——全部清点完毕。物料已备齐,下一章正式进入施工:用 Model2 结构把青梧书肆的分层立起来。