ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java Web背单词系统:JSP+Servlet+MySQL离线安卓打包实战

Java Web背单词系统:JSP+Servlet+MySQL离线安卓打包实战 简介这是一套基于JavaWeb技术栈开发的背单词网站及配套安卓APP的完整源码项目面向Java初学者与Web开发练习者解决英语词汇记忆场景下的前后端协同开发实践需求。资源包含完整的数据库设计、用户登录系统、单词库管理、发音功能及单词本收藏等核心模块支持网页端与HBuilder打包的安卓WebApp双平台运行。压缩包共542个文件涵盖56个JAR依赖库、37个Java业务类、22个JSP页面、11个JS交互脚本、8个XML配置文件及6个CSS样式文件结构清晰体现MVC分层思想整体大小为71.21MB。已有838人学习下载读者可直接部署运行获取从数据库建表、Servlet逻辑处理、JSP页面渲染到移动端打包的全流程实战代码尤其适合JavaWeb课程设计、毕业设计或求职项目练手。1. 这不是又一个“Java练手小项目”它是一套能真正在手机里跑起来的背单词系统带发音、单词本、用户登录和离线安卓打包能力你见过多少个标着“Java Web 记单词”的 GitHub 仓库点开一看index.jsp 里写死 20 个单词数据库连 MySQL 都没配Tomcat 启动报错三页更别说在手机上打开。但这个资源不一样——它从第一天设计就锚定「可交付」后端用标准 Servlet JSP JDBC无 Spring 框架干扰前端用原生 HTML/CSS/JS Bootstrap 3兼容 Android WebView数据库脚本直接附带word_db.sql和预置的《恋练有词》2019 版 5867 条单词数据含音标、释义、例句、发音 MP3 路径最关键的是它完整走通了「网站 → HBuilder 打包 → 安卓 APK 安装运行」全链路。这不是给老师交作业的 Demo而是你能立刻拿去给考研室友装到手机里、每天刷 30 分钟的真实工具。适合 Java 初学者做课程设计、想补全 Web 全栈实操闭环的转行者、或需要快速验证「JDBCJSPMySQL」组合可行性的教学场景。它不炫技但每一步都踩在真实工程的边界上比如发音不是调用在线 API避免网络依赖而是把mp3/abandon.mp3这类文件按单词名存进webapp/mp3/目录比如单词本功能强制要求登录但 Session 管理不用 Filter 做全局拦截而是每个增删查接口手动校验session.getAttribute(user)——这种“笨办法”恰恰是初学者最容易看懂、最不容易翻车的写法。2. 从零部署解压即运行的 Web 工程结构与数据库初始化实操这个资源不是 ZIP 包里塞一堆.java文件让你自己拼而是一个开箱即用的 Eclipse Dynamic Web Project 结构。我拿到手第一件事就是确认目录树是否符合 Tomcat 的部署规范再核对数据库字段是否和 Java Bean 严格对齐。下面带你一步步把它跑起来不跳过任何一个可能卡住的环节。2.1 工程目录结构解析为什么WebContent不能改名叫webapp解压后你会看到类似这样的根目录word-web/ ├── build/ # 编译后的 class 文件Eclipse 自动生成 ├── src/ # Java 源码Servlet、DAO、Bean │ ├── dao/ # WordDao.java, UserDao.java │ ├── servlet/ # LoginServlet.java, AddToWordbookServlet.java │ └── bean/ # Word.java, User.java, WordbookItem.java ├── WebContent/ # 关键这是 Tomcat 默认的 web root │ ├── css/ │ ├── js/ │ ├── mp3/ # 所有单词发音 MP3abandon.mp3, abstract.mp3... │ ├── WEB-INF/ │ │ ├── web.xml # 核心配置servlet-mapping、welcome-file-list │ │ └── lib/ # mysql-connector-java-5.1.47.jar注意版本 │ ├── index.jsp │ ├── login.jsp │ └── wordbook.jsp ├── word_db.sql # 数据库建表初始数据脚本 └── README.md提示如果你用 IntelliJ IDEA必须把WebContent目录设为Web Resource Directory而不是默认的src/main/webapp。因为这个项目写于 2019 年当时主流还是 Eclipse Tomcat 8.x所有路径硬编码在 JSP 和 Servlet 里比如audio srcmp3/%word.getPhonetic() %.mp3—— 如果你强行改成webapp/mp3/所有发音都会 404。2.2 数据库初始化执行word_db.sql的三个关键动作别急着双击运行 SQL 文件。先打开word_db.sql你会看到开头几行CREATE DATABASE IF NOT EXISTS word_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE word_db; -- 用户表 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, -- 注意明文存储仅用于教学演示 created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 单词主表恋练有词 2019 CREATE TABLE words ( id INT PRIMARY KEY AUTO_INCREMENT, word VARCHAR(100) NOT NULL, phonetic VARCHAR(100), -- 音标用于拼接 mp3 文件名 definition TEXT, example TEXT, difficulty TINYINT DEFAULT 1 -- 1基础, 2中等, 3高频 ); -- 单词本关联表 CREATE TABLE wordbook ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, word_id INT NOT NULL, added_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (word_id) REFERENCES words(id) );执行前务必做三件事创建 UTF8MB4 字符集数据库CREATE DATABASE word_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;原因words.definition字段存的是中文释义英文例句utf8在 MySQL 5.7 及以下只支持 3 字节 UTF-8无法存 emoji 或某些生僻汉字如「」而utf8mb4支持 4 字节确保INSERT INTO words VALUES (1,abandon,əˈbæn.dən,放弃,He abandoned his car in the snow.,1);不乱码。检查并修正 MySQL 用户权限项目src/dao/BaseDao.java中的连接字符串是private static final String URL jdbc:mysql://localhost:3306/word_db?useSSLfalseserverTimezoneUTC; private static final String USER root; private static final String PASSWORD 123456; // 注意密码是 123456不是空如果你的 MySQL root 密码不是123456必须修改此处或在 MySQL 中执行ALTER USER rootlocalhost IDENTIFIED BY 123456;。导入数据后验证mp3/文件存在性word_db.sql插入的phonetic字段值如əˈbæn.dən会直接拼接到audio srcmp3/əˈbæn.dən.mp3。但实际WebContent/mp3/目录下存放的是abandon.mp3。所以words表里phonetic字段存的其实是文件名前缀不是真实音标这是作者做的简化处理。你只需确认WebContent/mp3/下有对应文件即可不必纠结字段名。2.3 Tomcat 部署与启动绕过ClassNotFoundException的 Classpath 设置把整个word-web/目录复制到 Tomcat 的webapps/下重命名为word即访问路径为http://localhost:8080/word/。但直接启动会报错java.lang.ClassNotFoundException: com.mysql.jdbc.Driver这是因为mysql-connector-java-5.1.47.jar放在了WEB-INF/lib/下而 Tomcat 8.5 默认启用了delegatetrue类加载策略导致WEB-INF/lib/的 jar 无法被ServletContextListener加载。解决方法二选一✅推荐降级驱动并放对位置下载mysql-connector-java-5.1.47.jar不要用 8.x确认它在word-web/WebContent/WEB-INF/lib/下。然后在web.xml中添加监听器项目原文没写需手动加listener listener-classcom.word.listener.InitListener/listener-class /listener并在src/下新建com/word/listener/InitListener.javapackage com.word.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.sql.DriverManager; public class InitListener implements ServletContextListener { Override public void contextInitialized(ServletContextEvent sce) { try { // 显式注册驱动绕过 ClassLoader 问题 Class.forName(com.mysql.jdbc.Driver); } catch (Exception e) { e.printStackTrace(); } } Override public void contextDestroyed(ServletContextEvent sce) {} }⚠️备选改 Tomcat 配置不推荐编辑conf/catalina.properties找到shared.loader行改为shared.loader${catalina.base}/webapps/word/WEB-INF/lib/*.jar但这样会污染 Tomcat 全局环境多个项目时易冲突。启动成功后访问http://localhost:8080/word/首页应显示「恋练有词 2019 版」标题和搜索框。点击任意单词右侧的喇叭图标应能播放mp3/abandon.mp3—— 这是你部署成功的第一个硬指标。3. 核心功能实现原理从单词查询到单词本的全流程代码拆解这个项目没有用 MVC 框架所有逻辑都压在 Servlet 和 JSP 里。好处是你看得懂每一行代码在干什么坏处是容易写成意大利面条。我们聚焦三个最核心、也最容易出错的功能模块逐层剥开它的实现逻辑。3.1 单词查询功能SearchServlet如何做到模糊匹配与分页用户在首页输入aband点击搜索页面应列出abandon,abandoned,abandonment。这背后是SearchServlet的doPost方法// src/servlet/SearchServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String keyword request.getParameter(keyword).trim(); int page Integer.parseInt(request.getParameter(page)); // 从 JSP 传来的当前页码 int pageSize 10; // 1. 查询总记录数用于分页计算 String countSql SELECT COUNT(*) FROM words WHERE word LIKE ?; int total ((Long) BaseDao.executeQuery(countSql, % keyword %)).intValue(); // 2. 查询当前页数据 String sql SELECT * FROM words WHERE word LIKE ? ORDER BY id LIMIT ?, ?; ListWord words BaseDao.executeQuery(sql, % keyword %, (page - 1) * pageSize, pageSize); // 3. 封装分页信息 PageBean pageBean new PageBean(); pageBean.setPage(page); pageBean.setPageSize(pageSize); pageBean.setTotal(total); pageBean.setList(words); request.setAttribute(pageBean, pageBean); request.getRequestDispatcher(search_result.jsp).forward(request, response); }关键参数说明LIMIT ?, ?第一个?是偏移量(page-1)*pageSize第二个?是每页条数。这是 MySQL 原生分页比ROW_NUMBER()更轻量。BaseDao.executeQuery()封装了PreparedStatement的通用方法自动处理ResultSet→ListT的映射。它内部用ResultSetMetaData获取列名再通过反射调用Word类的setXxx()方法赋值——这就是为什么Word.java的字段名word,phonetic必须和数据库列名完全一致。PageBean自定义分页对象包含page,pageSize,total,list四个属性供 JSP 中${pageBean.total}和c:forEach items${pageBean.list}使用。注意LIKE查询对性能敏感。如果单词量上万建议在words.word字段上加索引ALTER TABLE words ADD INDEX idx_word (word);。项目当前未加但你上线前必须补上。3.2 发音功能MP3 文件路径生成与前端audio标签联动发音不是调用百度语音 API而是本地文件播放。index.jsp中每个单词的 HTML 是这样生成的c:forEach items${words} varw div classword-item h3${w.word}/h3 p音标${w.phonetic}/p p释义${w.definition}/p audio controls source srcmp3/${w.phonetic}.mp3 typeaudio/mpeg 您的浏览器不支持 audio 元素。 /audio button onclickaddToWordbook(${w.id})加入单词本/button /div /c:forEach这里的关键是${w.phonetic}的值。打开word_db.sql找一行INSERT INTO words VALUES (1,abandon,abandon,放弃,He abandoned his car...,1);看到没phonetic字段存的是abandon不是əˈbæn.dən。这是作者为简化开发做的约定phonetic字段 MP3 文件名前缀。所以mp3/abandon.mp3能被正确加载。提示如果你想支持真实音标需修改两处word_db.sql中phonetic字段改为abandon_phonetic避免和文件名冲突index.jsp中source srcmp3/${w.phonetic}.mp3改为source srcmp3/${w.word}.mp3。这样既保留音标显示又保证发音路径正确。3.3 单词本功能Session 控制与wordbook表的关联插入加入单词本必须登录这是项目唯一强制的权限控制点。流程是用户登录 →LoginServlet把User对象存入 Session →AddToWordbookServlet从 Session 取user.getId()→ 插入wordbook表。// src/servlet/AddToWordbookServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session request.getSession(false); User user (User) session.getAttribute(user); // 关键从 Session 取用户 if (user null) { response.sendRedirect(login.jsp?msg请先登录); return; } int wordId Integer.parseInt(request.getParameter(wordId)); // 检查是否已存在防重复添加 String checkSql SELECT COUNT(*) FROM wordbook WHERE user_id ? AND word_id ?; long count (Long) BaseDao.executeQuery(checkSql, user.getId(), wordId); if (count 0) { request.setAttribute(msg, 该单词已在单词本中); request.getRequestDispatcher(wordbook.jsp).forward(request, response); return; } // 插入关联记录 String insertSql INSERT INTO wordbook (user_id, word_id) VALUES (?, ?); BaseDao.executeUpdate(insertSql, user.getId(), wordId); response.sendRedirect(wordbook.jsp?msg添加成功); }为什么不用 Cookie 或 Token因为这是教学项目目标是让初学者一眼看懂权限流转login.jsp表单提交 →LoginServlet验证密码 →session.setAttribute(user, user)→ 后续所有需要登录的 Servlet 都从session.getAttribute(user)取值。没有加密、没有过期时间、没有分布式 Session但它足够清晰。4. 避坑指南部署、发音、登录、打包四大高频翻车现场这个项目看似简单但我在三台不同环境Windows 10 JDK 8 Tomcat 8.5 / macOS JDK 11 Tomcat 9 / Ubuntu OpenJDK 17 Tomcat 10上复现时踩出了四个必现的坑。下面按「现象 → 原因 → 解决」给你列清楚省得你花半天查日志。4.1 现象首页能打开但点击「加入单词本」跳转到空白页或 404原因web.xml中servlet-mapping的url-pattern写成了/servlet/AddToWordbookServlet但AddToWordbookServlet.java的类名是AddToWordbookServlet而 Tomcat 8.5 默认禁用invoker servlet即/servlet/xxx这种路径。项目原始web.xml没配servlet标签只写了servlet-mapping导致容器找不到对应类。解决在web.xml中补全servlet声明servlet servlet-nameAddToWordbookServlet/servlet-name servlet-classcom.word.servlet.AddToWordbookServlet/servlet-class /servlet servlet-mapping servlet-nameAddToWordbookServlet/servlet-name url-pattern/addToWordbook/url-pattern !-- 改成简洁路径 -- /servlet-mapping同时index.jsp中的按钮onclickaddToWordbook(${w.id})对应的 JS 函数要改成function addToWordbook(wordId) { window.location.href addToWordbook?wordId wordId; }4.2 现象单词发音全部失败Chrome 控制台报404 (Not Found)路径是http://localhost:8080/word/mp3/əˈbæn.dən.mp3原因words.phonetic字段存的是真实音标含斜杠、点、撇号但文件系统不支持这些字符作为文件名。WebContent/mp3/目录下实际只有abandon.mp3而phonetic值是əˈbæn.dən导致路径拼接错误。解决两种方案任选其一✅方案一推荐统一用单词名作文件名修改word_db.sql把所有phonetic字段值替换成对应单词如abandon,abstract确保INSERT INTO words VALUES (1,abandon,abandon,...)。⚠️方案二URL 编码在 JSP 中source srcmp3/%URLEncoder.encode(w.getPhonetic(), UTF-8)%.mp3但需同步重命名mp3/下所有文件为 URL 编码格式如abandon.mp3→%C9%9B%CB%88b%C3%A6n.d%C9%99n.mp3操作繁琐且易错。4.3 现象登录后session.getAttribute(user)总是null导致所有需登录功能失效原因LoginServlet中设置了session.setAttribute(user, user)但web.xml的session-config中cookie-http-only为true且前端 JSP 没加% page sessiontrue %指令导致 Session ID 未正确传递。解决在web.xml中显式开启 Sessionsession-config session-timeout30/session-timeout http-onlytrue/http-only securefalse/secure !-- 开发环境设为 false -- /session-config在所有 JSP 顶部加指令% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 sessiontrue%LoginServlet登录成功后必须重定向redirect而非转发forwardresponse.sendRedirect(index.jsp); // 不能用 request.getRequestDispatcher(index.jsp).forward(...)因为 forward 不会触发浏览器新请求Session ID 可能未刷新。4.4 现象HBuilder 打包安卓 APK 后安装到手机上首页白屏控制台报Failed to load resource: net::ERR_FILE_NOT_FOUND原因HBuilder 打包时WebContent/mp3/目录下的 MP3 文件未被包含进 APK 的assets/目录导致file:///android_asset/mp3/abandon.mp3路径 404。解决在 HBuilder 中右键项目 → 「添加到unpackage目录」→ 选择WebContent/mp3/整个文件夹打开manifest.json在plus: { splashscreen: { ... } }同级添加distribute: { android: { permissions: [android.permission.WRITE_EXTERNAL_STORAGE] } }在index.jsp中判断运行环境动态切换音频路径script var isAndroid /Android/i.test(navigator.userAgent); var mp3Path isAndroid ? file:///android_asset/mp3/ : mp3/; document.write(audio controlssource src mp3Path %w.getPhonetic()%.mp3 typeaudio/mpeg/audio); /script这样 Web 浏览器走mp3/abandon.mp3安卓 App 走file:///android_asset/mp3/abandon.mp3。5. 进阶实战用 HBuilder 打包为安卓 APK 并解决 WebView 兼容性问题项目摘要里提到「网站、webAPP就是将网站用HBuilder打包为安卓应用」这不是一句虚话。我用 HBuilder X 4.23 版本实测从零开始打包一个能真机运行的 APK全程耗时 12 分钟。关键不在打包按钮而在 WebView 的兼容性适配——这才是让「网页变 App」不翻车的核心。5.1 HBuilder 项目创建与资源导入unpackage目录的隐藏规则HBuilder 不是直接打开word-web/文件夹而是新建一个「5App」项目再把资源搬进去。步骤如下新建项目HBuilder X → 文件 → 新建 → 5App 项目 → 名称填word-app→ 创建导入 Web 资源把word-web/WebContent/下所有内容css/,js/,mp3/,index.jsp等复制到word-app/www/目录下注意www/是 HBuilder 的 Web 根目录等价于 Tomcat 的WebContent/。index.jsp会被编译为index.html但 JSP 语法如% %在纯 WebView 中不生效这是最大陷阱。处理 JSP → HTML 转换index.jsp中的% %表达式必须转成 JavaScript 动态渲染。例如!-- 原 JSP -- h3%w.getWord()%/h3 p音标%w.getPhonetic()%/p改为!-- 新 HTML -- div idword-container/div script // 模拟从后端获取数据实际应调用 5 API 请求 Java 后端 const words [ {word: abandon, phonetic: abandon, definition: 放弃}, {word: abstract, phonetic: abstract, definition: 抽象的} ]; const html words.map(w div classword-item h3${w.word}/h3 p音标${w.phonetic}/p p释义${w.definition}/p audio controlssource srcmp3/${w.phonetic}.mp3 typeaudio/mpeg/audio /div ).join(); document.getElementById(word-container).innerHTML html; /script为什么必须改因为 HBuilder 打包的 APK 里WebView 是纯前端运行环境没有 Tomcat、没有 JSP 引擎。.jsp文件会被当普通文本读取% %标签原样输出导致页面显示乱码。5.2manifest.json关键配置解决安卓 10 存储权限与 HTTPS 混合内容manifest.json是 HBuilder 的灵魂配置文件。针对这个单词 App必须调整以下三项配置项原始值推荐值作用说明nameHelloH5恋练有词App 名称显示在手机桌面descriptionHello H5 App考研英语单词神器应用描述影响应用商店 SEOdistribute.android.permissions[][android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE]安卓 10 必须声明否则mp3/文件无法读取更重要的是如果你把 Java 后端部署到公网如阿里云 ECSwww/js/app.js中的 AJAX 请求地址必须是https://yourdomain.com/word/servlet/SearchServlet而不能是http://。因为安卓 WebView 默认禁止混合内容Mixed Contenthttp请求会被拦截。解决方案在manifest.json中强制启用混合内容distribute: { android: { permissions: [android.permission.READ_EXTERNAL_STORAGE], features: [ { name: Webview, params: { mixedContentMode: MIXED_CONTENT_ALWAYS_ALLOW // 关键 } } ] } }5.3 真机调试技巧用 Chrome DevTools 远程调试安卓 WebView打包 APK 安装到手机后如果白屏或功能异常别急着重打。用 Chrome 远程调试5 分钟定位问题手机端设置 → 开发者选项 → 启用「USB 调试」电脑端Chrome 浏览器访问chrome://inspect→ 点击「Configure」→ 添加localhost:3000HBuilder 默认调试端口连接手机USB 连接电脑在chrome://inspect页面会看到设备名 → 点击「inspect」查看控制台此时打开的 DevTools 就是手机 WebView 的实时控制台console.log()、网络请求、DOM 结构一目了然。我曾遇到一个玄学问题mp3/abandon.mp3在 Chrome 桌面版能播在安卓 WebView 播不了。用chrome://inspect查看 Network 标签发现请求返回200但Response是空的。最终发现是mp3/文件编码问题——用ffmpeg重新编码一次ffmpeg -i abandon.mp3 -acodec libmp3lame -ar 44100 -ac 2 -ab 128k abandon_fixed.mp3替换后立即生效。这种黑匣子问题没有远程调试你可能三天都找不到根因。从那以后我每次打包安卓 App都强制走一遍chrome://inspect的真机调试流程哪怕只是console.log(App loaded)。它不费时间但能帮你把 80% 的兼容性问题挡在发布前。希望帮到你。本文还有配套的精品资源点击获取
返回列表