简介:WADE开发文档面向使用WADE开发框架进行企业级Web应用搭建与维护的初中级开发者,聚焦于项目快速初始化与框架升级后的改造适配两大常见需求。压缩包内共1个doc文件,约3.24MB,按基本功能、配置文件说明、版本改造点等顺序组织,可作为日常开发时的对照查阅材料。文档已吸引973人学习下载,具备一定的参考基础。内容上,先以quickstart与ant newproject为例说明空项目生成流程,包括build.properties中PROJECT_NAME、PROJECT_HOME、WADELIB_HOME等参数的填写,以及ant all与ant allwar两种编译方式的区别;随后逐条梳理WADE2.50至2.59各版本的建议改造点,如JDK_VERSION配置、输入框enterAction回车控制、集群缓存共享的ehcache.xml调整、客户端DataMap与DatasetList数据格式统一、Buttom页面的拼写修正等,并补充新增数据功能的HTML、Java事件与Bean编写次序。读者可据此理解框架目录结构与配置约定,减少升级迁移中的踩坑,也能把它当作交接或新人上手时的速查手册。
1. 先确认 quickstart 和 wadelib 是不是同级,再动手敲 ant newproject
照着 WADE 开发文档搭环境,多数人第一次安装会失败在ant newproject这一步:命令敲完,build 目录里什么也没有;或者项目生成了,一编译就报找不到 wadelib。WADE 这套 Java Web 框架的环境搭建门槛不在命令本身,而在几个必须先确认的前提:quickstart 和 wadelib 目录是否同级、JDK 用 1.4 还是 1.5、APPLANG写成 GB2312 之后源文件编码有没有跟着改。这几项任意一个不对,后面的编译、打包、部署就会接连报错。
这篇笔记按我实际安装过的一套顺序写:先摆放环境,再修改 build.properties,然后依次执行 ant newproject、ant all、ant allwar,最后用 VIP 客户资料的新增、修改、删除、导入流程验证环境是否真的跑通。适合刚接手 WADE 项目、需要从零把工程启动起来的人,也适合正在做旧版本升级改造的人。
2. 准备 JDK 与 Ant:版本和目录摆放先定死
这套框架所在的时期,Java 生态还没有统一的依赖管理,Ant 是唯一的构建入口,jar 包依赖靠lib目录手动指定。因此安装 WADE 时,首先要处理的问题都是 JDK 和 Ant 的基础配置。我遇到过几次项目无法编译,问题都不在代码,而在于环境准备阶段的假设和实际不符。
2.1 JDK 版本:先确认运行时版本,再确定编译参数
WADE 2.59 之前,编译参数分散在 build.xml 里,改一次要翻几百行。2.59 之后,build.properties里多了一个JDK_VERSION=1.4|1.5,专门用来指定编译应用使用的 Java 版本。这个参数容易被忽略,但它决定了打出来的 class 文件版本号:javac 1.5 编出来是 49,JDK 1.4 的 JVM 只认到 48,在 JDK 1.4 的运行环境下部署就是UnsupportedClassVersionError。
因此顺序要先反着来:先询问运行环境是哪个 JDK,再回来定JDK_VERSION。开发机上装 1.5 来写代码没有问题,但最终打包时必须把JDK_VERSION设成 1.4,让 Ant 把-source 1.4 -target 1.4传给 javac。我一般还会在打包机上留一份 1.4 的 JDK,专门做一次真实部署验证,不然本地能跑、测试环境不能跑。
# 三个输出必须能对上,否则后面全是坑 echo $JAVA_HOME # 指向 JDK 根目录,不是 jre 目录 java -version # 运行时版本,部署目标的底线 javac -version # 编译器版本,决定 class 文件版本号JAVA_HOME写错是最常见的一种:很多人指到jdk/jre子目录,java命令照样能跑,但 Ant 加载 tools.jar 时找不到,报Unable to find a javac compiler。java -version和javac -version不一致也要留意,说明 PATH 里有多个 JDK,实际编译用的可能不是你预期的那个。
2.2 Ant 与 ant-contrib.jar:开发时不需要,打包时必须装
Ant 的版本不必追新,能从 build.xml 里正常加载 taskdef 就行,我一般直接沿用工程里带的或者组内统一的那一个,换版本反而容易引入 task 行为差异。安装本身很简单,解压后把ANT_HOME指到解压根目录,把bin加进 PATH。
真正容易埋下隐患的是ant-contrib.jar。开发时用ant all完全用不到它,但一旦执行ant allwar打 war 包,build.xml 里扩展的 if/unless 之类 task 就会去加载这个 jar,缺了直接报Could not create task or type of type。这个 jar 要放在Ant 的全局 lib 目录,不是项目的web/WEB-INF/lib;后者是运行时 classpath,Ant 启动时根本看不到。
# ANT_HOME 指向解压根目录,不是 bin 子目录 export ANT_HOME=/opt/apache-ant export PATH=$ANT_HOME/bin:$PATH # 校验 Ant 可用 ant -version # ant-contrib.jar 放到全局 lib,Ant 启动时自动加载 cp ant-contrib.jar $ANT_HOME/lib/ ls -l $ANT_HOME/lib/ant-contrib.jar参数含义:ANT_HOME是 Ant 寻找自身类库和lib目录的基准,写错这一项,后面即使放了 jar 也加载不到;cp到$ANT_HOME/lib是全局生效的做法,如果不想改动全局环境,也可以用ant -lib /path/to/ant-contrib.jar allwar在命令行临时指定,但每台机器每次都要记得加,在团队协作里更容易遗漏。ls那行是确认文件真的到位,不是复制到别的目录去了。
提示:
ant-contrib.jar在 build.properties 注释里提到过下载位置,实际取包时请跟组内的构建机保持同一份,不要各自找不同来源的版本,构建结果不一致时极难排查。
2.3 目录摆放:wadelib 与工程同级最省事
build.properties里有一行WADELIB_HOME = ../../../wadelib/wadelib,这个相对路径是按“wadelib 和应用目录同一级”来写的。同级时所有相对路径都成立,Ant 找 jar、找build_wadelib.xml都不会绕弯。
| 目录 | 建议位置 | 作用 |
|---|---|---|
| 应用工程 | 如work/custmgr | 你自己的业务工程,newproject 生成或手写 |
| wadelib | 如work/wadelib | 框架类库和构建脚本所在,被应用引用 |
| Ant | 全局任意路径 | 构建入口 |
| JDK | 全局任意路径 | 编译与运行 |
# 推荐的同级摆放,执行 ant 时工作目录在工程的 build 下 # work/ # ├── custmgr/ <- 应用工程 # │ ├── build/ # │ │ ├── build.properties # │ │ └── build.xml # │ ├── etc/ # │ ├── src/ # │ └── web/ # └── wadelib/ <- 与 custmgr 同级 # ├── build/build_wadelib.xml # └── wadelib/web/WEB-INF/lib/如果 wadelib 因为历史原因没有放在同级,不能只改WADELIB_HOME就结束,build/build.xml里引入build_wadelib.xml的那一行也要同步修改,否则编译时框架的公共 target 根本加载不进来:
<!-- build/build.xml 片段:引入 wadelib 的构建脚本 --> <!-- 默认认为 wadelib 与工程同级,路径就是 ../../wadelib/build/build_wadelib.xml --> <import file="../../wadelib/build/build_wadelib.xml"/>这里的file是相对于当前 build.xml 所在目录解析的,不是相对于项目根目录解析。很多人按项目根目录去数../的层数,结果少一层,Ant 会直接报Cannot find ../../wadelib/build/build_wadelib.xml。改完这一行还要回头核对WADELIB_HOME,两个路径必须指向同一份 wadelib,否则编译用的 jar 和运行时加载的 jar 可能不是同一批,出现NoClassDefFoundError时几乎无法排查。
3. 用 ant newproject 生成新工程:build.properties 逐项改
环境摆放好之后,新建项目这一步就是“复制模板 + 全文改名”。听起来简单,实际改动点集中在 build.properties 一个文件里,改错一项,整个工程的包名、类名前缀、打包产物名都会跟着错。我见过最麻烦的一次是NEW_NAME1用了带下划线的名字,生成的包路径和 Java 命名规范冲突,编译到一半才暴露。
3.1 build.properties 每个参数都要知道在改什么
这个文件是整套构建的总开关,newproject 和日常编译都读它。把它拆开看,其实只有三类:项目身份、目录路径、编码与编译版本。
# 编译时的省代码,开发时用公用,打包部署时选择对应的省份 PROVINCE_NAME = common # 项目名称,打出来的 jar / war 都以这个名称为准 PROJECT_NAME = wade-quickstart # 项目目录,.. 表示当前 build 目录的上一级 PROJECT_HOME = .. # WADELIB 包目录,newproject 之前先确认这一行指向对不对 WADELIB_HOME = ../../../wadelib/wadelib # 六个路径变量,目录结构没变就不要动 build = ${PROJECT_HOME}/build config = ${PROJECT_HOME}/etc source = ${PROJECT_HOME}/src classes = ${PROJECT_HOME}/web/WEB-INF/classes lib = ${WADELIB_HOME}/web/WEB-INF/lib web = ${PROJECT_HOME}/web # 编译编码,必须跟源文件实际编码一致 APPLANG = GB2312PROVINCE_NAME是省代码,开发期统一写common,只有打包部署到具体省份时才改。它的用途是让同一套代码在多省份部署时走不同的配置分支,所以不要在开发阶段随便填一个省份名,否则本地调试走的是省份分支,跟公用逻辑对不上,测试环境一部署就出错。
PROJECT_NAME决定最终 jar、war 的文件名,也常被当成应用标识使用,改名之后记得同步检查部署脚本里有没有硬编码的旧名。PROJECT_HOME = ..是相对 build 目录往上一层,也就是说这个文件必须放在build/下执行才有意义,在项目根目录直接敲 ant 是找不到配置的。
APPLANG = GB2312是最容易出问题的一行。它告诉编译器源文件编码,如果 IDE 里源文件实际存的是 UTF-8,编译出来的中文就是乱码,页面上显示成一串问号。判断方法很简单:用编辑器打开一个含中文的 java 文件,看它识别的编码和这里写的是否一致,不一致就统一掉,别指望编译器帮你猜。
3.2 OLD_NAME 与 NEW_NAME:三组大小写形态对应三类标识
newproject 的本质是把 quickstart 整个拷一份,然后按三组名字做全文替换。之所以要三组,是因为 Java 工程里同一个业务名会以三种形态出现:包名和目录名用小写,类名用首字母大写,常量或配置 key 用全大写。少配一组,就会在某个位置留下旧名。
# 原系统的小写名称:包名、目录名、jar 名 OLD_NAME1 = quickstart # 原系统的首字母大写名称:类名、页面类名 OLD_NAME2 = Quickstart # 原系统的大写名称:常量、配置 key OLD_NAME3 = QUICKSTART # 新系统的小写名称 NEW_NAME1 = custmgr # 新系统的首字母大写名称 NEW_NAME2 = Custmgr # 新系统的大写名称 NEW_NAME3 = CUSTMGR三项之间的对应关系不能错位:NEW_NAME1必须全小写,NEW_NAME2必须严格首字母大写、其余小写,NEW_NAME3必须全大写。常见错误是把NEW_NAME2写成CustMgr这种驼峰,替换之后类名是合法的,但引用它的地方按Custmgr去找,编译报cannot find symbol,而且错误信息指向的是使用方,不是定义方,排查方向容易跑偏。
另外,NEW_NAME1一旦确定就不要再改回去重跑 newproject。因为替换已经写进 java 文件的 package 声明和 import 里了,重跑会在已经改过名的工程上再替换一次,生成一堆半新半旧的文件。
3.3 执行 ant newproject 与替换后的自检
配置改完,进入 build 目录执行生成。命令不复杂,但执行完一定要自己看一眼产物,别假设它成功了。
# 进入 build 目录执行,配置文件就是当前目录下的 build.properties cd custmgr/build ant newproject # 生成的新工程落在 build 目录下,目录名以 NEW_NAME1 为准,先确认存在 ls -d */ # 全量搜一遍旧名,确认没有漏替换的地方 grep -rn -i "quickstart" . | head -20ant newproject读取的就是同目录的 build.properties,所以必须在 build 下执行;换到别处执行,Ant 会找不到 target 或读到另一份配置。ls -d */是确认新工程目录真的生成出来了,有时候中途报错但目录已经建了一半,看着像成功实际不完整。
grep那一步是我每次都会做的:只要还能搜到quickstart(不区分大小写),说明替换逻辑有漏,常见于混合大小写形态(比如QuickStart)或者被转义过的字符串。这时候不要手工一个个改,回到配置里补上对应的 OLD_NAME 形态重跑更稳妥。
注意:如果 grep 出来的结果集中在
page.application和sidebar.html里,那属于正常的模板残留,下一步统一清理,不算替换失败。
3.4 清理 quickstart 残留:page.application 与 sidebar.html
新工程生成之后,里面还带着 quickstart 的示例页面配置和菜单项。这两处不清理,编译是能过的,但会有两个后遗症:一是菜单里出现一堆示例入口,点进去报 404 或者跳到示例页面的类;二是page.application里重复的页面名配置可能和你新加的页面抢映射,表现为访问自己的页面却打开了示例页。
按文档的步骤走,清理动作有三处:
- 打开
page.application,删掉 quickstart 保留的示例页面名称配置。 - 打开
sidebar.html,删掉对应的菜单配置项。 - 如果还有示例相关的模板、图片、脚本目录,一并删掉,避免打包体积虚高。
# 先定位这两个文件的实际路径,不同版本放的层级不完全一样 find . -name "page.application" -o -name "sidebar.html" # 定位到之后,看里面哪些配置属于模板示例,按行号确认后再删 grep -n -i "examples\|quickstart" web/WEB-INF/page.application grep -n -i "examples\|quickstart" web/sidebar.html这里用grep -n是为了拿到行号,先逐行确认再删除。直接整段删掉的风险是误删框架必需的公共页面配置,比如登录页、错误页这类基础页面,删完启动就白屏。我一般采用的做法是:跟业务无关的示例页配置整段删,框架公共页面配置只删菜单项、保留页面注册。
3.5 ant all 与 ant allwar:开发编译和发布打包是两条路
工程清理干净后,第一件事是编译验证。这里有两套 target,用途完全不同,混用会白等很久。
cd custmgr/build # 开发用:默认 target,编译到 web/WEB-INF/classes,改完代码重跑 ant all # 测试打包发布用:打成 war,依赖 ant-contrib.jar ant allwarant all是默认 target,只做编译,输出到web/WEB-INF/classes,改一行代码重跑一次,几秒钟的事,适合边改边调。它不会打 war,也不会把etc下的配置塞进包,所以千万不要拿ant all的结果去做部署。
ant allwar才是打包发布用的,它会走完整的打包流程,把类文件、依赖 jar、配置一起塞进 war。这条命令依赖ant-contrib.jar,2.2 节那步没做的话,这里就是第一次出错的地方,报错信息通常在 taskdef 附近,看到Could not create task or type直接回去补 jar 就行,不用怀疑 build.xml。
打包前还有一件事要确认:PROVINCE_NAME是不是目标省份。开发期它是common,打包部署要改成对应省份,否则打出来的包里还是公用配置。这个值不会在报错里提醒你,只是在运行时逻辑对不上,属于典型的环境准备问题。
4. 框架版本改造:从 WADE2.5 到 2.59 的落地清单
新工程跑起来只是及格线。真正让环境能用起来的是按版本把改造点补齐——WADE 的多个版本都在原来的用法上加了建议方式,这些建议方式不是可有可无的优化,有几条属于不改就出故障的类型。下面按版本倒序整理,每一项都给出改法。
4.1 WADE2.59 与 2.56:JDK_VERSION 和输入框回车控制
2.59 的改造点就是那个JDK_VERSION。它解决两个问题:一是明确编译使用的 Java 版本,二是让 JDK 1.4 运行环境下也能部署。
# build.properties 中新增,取值只能是 1.4 或 1.5 # 目标是 JDK 1.4 运行环境时写 1.4,让 javac 按 1.4 生成 class JDK_VERSION = 1.4参数只有两个合法取值,写 1.4 表示按低版本编译、兼容性优先;写 1.5 表示可以用泛型、增强 for 这些语法,但产物不能在 1.4 的 JVM 上跑。判断依据永远是运行环境,不是开发机的 JDK 版本。写错之后的表现很典型:本地一切正常,部署到老环境启动就抛UnsupportedClassVersionError,看到这个错误先回来看这一行。
2.56 的改造点是输入框组件的回车动作。弹出窗口、金额输入、文本输入、下拉表格这些组件都提供了enterAction参数,以前各自用别的方式处理回车,写法五花八门,现在统一收口到这一个参数上。
<!-- 输入框回车时执行指定动作,替代过去散落在各处的键盘事件绑定 --> <input jwcid="@wade:TextField" value="ognl:info.CUST_NAME" enterAction="alert('请先选择客户')"/>enterAction里放的是回车触发时执行的动作表达式,可以是提示、可以跳转,具体按组件的实际语义来。改造成这种方式的好处是行为集中在一个参数上,排查“为什么这个框回车没反应”时不用去翻页面脚本。
4.2 WADE2.55:集群下的缓存共享,ehcache.xml 增量合并
这条是集群部署必须处理的。老版本的缓存是纯本地内存,单机没问题,一上集群就出问题:A 节点改了数据,B 节点的缓存还是旧的,用户在多台机器之间跳来跳去,看到的数据一会儿新一会儿旧。新版本支持集群下缓存共享,但前提是每个应用都更新到 Quickstart 2.35 及以上版本。
改法不是整份替换,而是取增量合并:
<!-- etc/common/ehcache.xml 片段:以 quickstart 的增量为准,自定义 cache 保留 --> <ehcache> <!-- 框架公共缓存,从 quickstart 的增量配置里搬过来 --> <cache name="wadeCommonCache" maxElementsInMemory="1000" eternal="false" overflowToDisk="false" timeToIdleSeconds="300" timeToLiveSeconds="600"/> <!-- 业务自定义缓存保持原样,只补框架新增的那几段 --> <cache name="custmgrVipCache" maxElementsInMemory="500" eternal="false" overflowToDisk="false" timeToIdleSeconds="120" timeToLiveSeconds="300"/> </ehcache>合并时的原则是:quickstart 里新增的 cache 定义照搬,自己项目里已有的 cache 保留不动,timeToIdleSeconds、timeToLiveSeconds这类参数按业务容忍度调,不要跟着模板直接抄——缓存时间太长,集群同步的延迟就会被放大,用户感知到的还是“数据没更新”。改完要两个节点同时起,改一台看不出效果。
4.3 WADE2.53 与 2.52:客户端数据格式统一和 Buttom 改名
2.53 提供了客户端DataMap、DatasetList,跟服务器端的方法和数据转换格式保持一致。同时提供了把服务器端数据通过setAjaxDataset之类的方式传到客户端,客户端直接拿到DatasetList操作。
// 客户端统一使用 DataMap / DatasetList,与服务器端数据结构对齐 var ds = new DatasetList(); var row = new DataMap(); row.put("VIP_ID", "1001"); row.put("CUST_NAME", "示例客户"); ds.add(row); // 遍历和取值方式与服务器端一致,不需要再做一次格式转换 for (var i = 0; i < ds.size(); i++) { var item = ds.get(i); console.log(item.get("CUST_NAME")); }统一成这两种格式之后,前后端不再各写一套数据组装逻辑,局部刷新场景用这种传递方式比整页刷新性能更好。改造时优先处理现有的 ajax 操作,单一或较少的局部刷新最能体现收益;大表格整页刷新反而没必要改,投入产出不划算。
2.52 这条属于纯改名,但风险不低:web/Buttom.html、view/Buttom.java要改成Bottom.html、Bottom.java,page.application里的配置也要同步改,否则底部页面直接报错。看起来只是拼写修正,实际是个跨平台问题——Windows 文件系统不区分大小写,改了名本地照样能跑;部署到区分大小写的 Linux 上,容器按Bottom.html去找,文件实际叫Buttom.html,直接 404。这一条是不容易事先想到的经验。
4.4 WADE2.5:初始脚本、DOM 获取方式和 PageSubmit
2.5 这一版改动最多,也最容易改漏。三件事:初始脚本调用方式、DOM 对象获取、表单提交组件。
初始脚本以前是在页面里写<script>init();</script>手动调,写法不规范也加大代码量。新方式是每个页面载入时自动调用一个按页面名生成的默认方法,比如页面名是examples.basic.VipcustEdit,对应的初始方法就是initVipcustEidt()。
<!-- 通过 @wade:Head 的 initScript 参数指定自定义初始方法名 --> <span jwcid="@wade:Head" initScript="initVipcustEdit"/> <script> // 页面载入后自动调用,方法名与页面名对应;未编写时页面载入会忽略 function initVipcustEdit() { // 页面初始化逻辑放这里 } </script>initScript不写就走默认的按页面名生成规则,写了就用你指定的名字。注意如果方法名写错,页面不会报错,只是初始化逻辑静默不执行,表现出来就是“某些下拉框没数据”“某些字段没赋初值”,属于比较难查的一类问题。
DOM 获取也规范了:统一用getElement(name)、getElements(name)、hidden(name|obj)、disabled(name|obj),禁止再写document.getElementById(name).disabled = true。原因很实在:很多组件并不是单个 DOM 对象,比如日期组件包含一个文本框和一个图标,直接对 id 元素操作只会隐藏文本框,图标还留在页面上。这类问题在页面上看着就是“禁用没生效”,实际是操作对象搞错了。
linkSubmit(page, listener, params)改成用@wade:PageSubmit组件替代,因为前者在不同浏览器下存在无法跳转的问题。改的时候连同 listener 的写法一起对齐,别只换了标签名。
<!-- 用 PageSubmit 替代 linkSubmit,listener 指向页面类的同名方法 --> <a jwcid="@wade:PageSubmit" value="删除" listener="ognl:listeners.deleteVipcust" desc="VIP 客户资料" class="e_bLinkDelete" onclick="return queryBox(this, 'vips');"/>listener指向页面类里的方法名,desc用于确认框的提示文案,onclick里的queryBox是删除前的二次确认,返回 false 时不提交。这一整套替换完之后,浏览器兼容性问题基本消失,但要注意 listener 方法签名必须和组件期望的一致,签名不对的表现是点击无反应,也不报错。
5. 避坑与排查:编译、跳转、导入这三类最容易出问题
下面五条都是我在实际搭环境和帮别人排查时反复遇到的,按“现象 → 原因 → 解决”记录,遇到同类问题可以直接对照。
5.1 编译报找不到 wadelib 里的类
现象:ant all跑到编译阶段,报package xxx does not exist或者cannot find symbol,指向的是框架里的类。
原因:build.xml里 import 的build_wadelib.xml路径写死成../../wadelib/build/build_wadelib.xml,而实际 wadelib 不在同级目录。改配置的人只改了WADELIB_HOME,忘了改 import 路径,两处指向不同位置。
解决:回头核对两个路径是否指向同一份 wadelib,import 的file是相对当前 build.xml 解析的,按层数重新数一遍。改完执行ant all前先 clean 一次,避免旧 class 残留掩盖问题。
5.2 页面中文全是乱码
现象:页面能打开,中文显示成问号或方块。
原因:APPLANG写的是 GB2312,但源文件实际存的是 UTF-8,编译器按 GB2312 去读,字节被错误解释。
解决:统一编码。要么把源文件转成 GB2312(项目历史遗留是这套编码时只能这么做),要么把APPLANG改成 UTF-8 并确认容器和页面的编码声明一致。改完必须整体重新编译,只重编改动的文件不够,旧的 class 还会带着乱码进去。
5.3 ant allwar 报 taskdef 加载失败
现象:ant all一切正常,ant allwar一执行就报Could not create task or type of type。
原因:ant-contrib.jar没放,或者放到了项目的web/WEB-INF/lib下面。后者是运行时 classpath,Ant 启动时不会扫描。
解决:把 jar 放到$ANT_HOME/lib,或者用ant -lib /path/to/ant-contrib.jar allwar临时指定。放全局之后记得ant -version确认没用错 Ant 实例。
5.4 修改数据后,未提交的字段被清空
现象:只改了一个客户名称,保存之后发现电话、地址这些没在表单里提交的字段全变空值了。
原因:Bean 里用了update而不是save。update会把 data 里的所有数据写进表,字段在 data 里没指定就被清空;save会先查出原记录,再覆盖需要更新的部分。
// 只做部分字段更新,必须用 save return dao.save("TF_F_CUST_VIP", data, new String[] {"VIP_ID", "REMOVE_TAG"}, values); // data 已包含全部需要修改的信息时,用 update 效率更高 // dao.update("TF_F_CUST_VIP", data, new String[] {"VIP_ID", "REMOVE_TAG"}, values);判断标准很直接:data 是不是完整的一份记录。是,用 update;只带几个字段,用 save。用错方向基本就是数据被清空的后果,而且不容易第一时间发现。
5.5 Excel 导入后台拿不到文件流
现象:页面上选了文件点导入,后台pd.getUploadFile("FILE_PATH")拿到的流是空的,或者直接报空指针。
原因:表单少了enctype="multipart/form-data",文件根本没用流的方式提交;或者页面上没写verifyAll、confirmForm、confirmInfo这套标准验证方法,后台取不到数据。
解决:表单属性补上,<input type="file" name="FILE_PATH"/>的 name 要和后台取的 key 一致;页面类里把标准验证方法补齐,缺一个都会导致取不到数据。这条在文档里被单独标出来过,是很容易漏掉的一项。
6. 装完怎么验证跑通:用一条 VIP 客户资料流程自检
环境能不能用,不看 Ant 有没有报 BUILD SUCCESSFUL,而看一条完整的数据流程能不能走通。我一般用 VIP 客户资料这一套示例做自检,因为它刚好覆盖新增、查看、修改、删除、导入五个动作,每个动作都能验证到不同的环境要素。
| 步骤 | 动作 | 验证的环境点 |
|---|---|---|
| 1 | 打开列表页,点【新增】跳转到编辑页 | 页面映射、page.application 配置是否干净 |
| 2 | 填表提交,看是否提示新增成功 | 表单组件、页面类与 Bean 的调用链 |
| 3 | 列表里点【查看详情】 | 参数传递、主键绑定是否正确 |
| 4 | 改一个字段保存,回查其他字段是否还在 | save 与 update 的选择、字段映射 |
| 5 | 选中多行批量删除 | 数据集操作、确认框链路 |
| 6 | 下载模板,填两行导入,其中一行故意填错 | 上传表单 enctype、导入配置校验、错误文件下载 |
第 6 步是整个自检里信息量最大的一步,因为它一次验证了文件上传、配置校验、错误回写三条链路。导入配置里那行type建议统一设成1,也就是全部按文本处理:如果某一列设成时间类型并指定了格式,导出失败数据时会把原始输入2007-03-01转成2007-03-01 00:00,用户拿回来对照原始表格就对不上了。失败数据那份额外配置里要加上IMPORT_ERROR列,否则下载下来的错误文件只有数据、没有原因,等于没有提供有用信息。
// 导入后的标准流程:先解析,再执行业务,有失败数据就生成错误文件回传 IDataset dataset = ExcelParser.importExcel(pd, "examples/basic/VipcustList.xml", pd.getUploadFile("FILE_PATH").getStream())[0]; IDataset failds = bean.importVipcusts(pd, dataset); if (failds.size() != 0) { // 失败数据按导入配置回写成 excel,供用户下载后修正重传 String file_id = ExcelParser.writeExcelByImport(pd, "examples/basic/VipcustImport.xml", new IDataset[] { failds }); redirectToMsgByPopup("共导入" + dataset.size() + "条,成功" + (dataset.size() - failds.size()) + "条,失败" + failds.size() + "条"); } else { redirectToMsgByPopup("批量导入成功,按【关闭】关闭窗口回到主页面"); }importExcel的第一个参数是导入配置的相对路径,第二个参数是文件流;返回结果是个数组,取第 0 个元素才是数据集。writeExcelByImport负责把失败数据集按配置回写成 Excel,返回的文件标识用于后续生成下载链接。这段逻辑跑通,说明上传、解析、写回三个环节都正常。
自检做完还有一件事值得花五分钟:把PROVINCE_NAME从common改成目标省份,重跑一次ant allwar,把 war 包扔到一台干净的测试机上部署一遍。开发机上用ant all跑通,和打出来的 war 在别的机器上跑通,中间隔着编码、路径、缓存、JDK 版本四道坎,任何一道没跨过去,交付那天就得现场救火。
我踩过的坑里最费时间的一次,是 Windows 上把Buttom.html改名成Bottom.html就以为完事,部署到 Linux 上底部页面直接 404,日志里只有一行找不到模板。从那以后我每次搭完环境,都会强制走一遍“干净机器部署”这一步,把开发机的假设全部打掉。希望帮到你。
本文还有配套的精品资源,点击获取