基于JSP的高校财务处理系统:从设计到War包部署全流程复盘
又是一个被毕业设计折腾到怀疑人生的季节。每年这时候后台都会涌进来一批“基于某某技术的某管理系统设计与实现”的问题而“基于JSP的高校财务处理系统”这个题目出镜率尤其高。原因很简单JSP老归老但它在高校课程体系里根深蒂固很多学校的Java Web课程就是以JSP/Servlet为核心的再加上“财务处理”这个业务领域足够正规、足够有区分度随便一个模块拎出来都能写出几千字的需求分析非常适合当毕业设计题目。但“适合做题目”不等于“好做”。我接手过不少类似的传统JSP项目自己也完整实现过这个财务系统。说实话用JSP这种“上古技术栈”做一个带权限、带审批流、带报表导出的系统踩坑的地方比想象中多得多。这篇文章不是给你贴几百行代码就完事而是把我从选题拆解、数据库设计、代码实现、优化细节到War包部署的完整过程复盘一遍里面有大量实测过的小技巧和失败教训特别适合准备用“JSP Servlet MySQL Tomcat”这套组合做毕设的同学直接参考。1. 项目整体设计与技术选型1.1 为什么选JSP而不是Spring Boot先说一个很多人纠结的问题现在企业里早不用JSP写新项目了为什么毕业设计还选它我个人的看法是毕业设计考察的是“你对所学知识的综合运用能力”不是“你能不能跟上最新框架”。Spring Boot当然香但它帮你屏蔽了太多底层细节内嵌Tomcat、自动配置、起步依赖一顿操作下来系统能跑但问你怎么处理Session、怎么写Servlet映射、怎么用Filter做登录拦截你可能答不上来。而JSP Servlet这套组合天然逼着你把每一次请求的生命周期、每一个数据的流转过程都摸清楚这在答辩的时候反而是加分项。再加上很多学校的课程设计和期末项目都是JSP起步选它意味着你不需要花一个月去补Spring Boot全家桶的基础可以把精力集中到业务逻辑上。换句话说用JSP做财务系统是在“你熟悉的技术范围”内找一个“能体现工作量”的业务场景这是性价比最高的路线。当然如果你现在已经熟练SSM或者Spring Boot我也不是劝你放弃。但如果你连Maven依赖冲突都还没搞明白老老实实回到JSP JDBC反而更稳。传统项目的优势是“链路短”浏览器请求 → Servlet → DAO → MySQL每一步都可以在纸上画出来排查问题也直观这种清晰度在答辩现场非常讨喜。1.2 高校财务系统的需求范围与控制“财务处理系统”这个名字很有迷惑性听起来要做成用友、金蝶那种级别的产品。这里一定要控制范围否则把你扔进财务业务里三个月都出不来。高校财务实际涉及的场景极多预算编制、指标控制、借款管理、报销审核、凭证生成、工资发放、学费收缴、票据管理……全做显然不现实。作为一个毕业设计核心目标是打通“业务闭环”而不是覆盖“全流程”。我当时的做法是画了一张业务流程图以“资金流动”为主线只做四个核心环节。第一是用户登录与权限管理区分管理员、财务人员、普通教职工/学生三种角色第二是缴费管理面向学生的学费、住宿费、考试费等收费项目第三是报销管理面向教职工的日常报销单提交和审核第四是预算与统计报表管理员能按时间、按部门查看支出和缴费情况并导出Excel。这样一圈下来后台管理、业务流转、审批状态、数据统计全都有了工作量适中故事也讲得完整。很多人忽略的一点是非功能需求也要写进设计说明。比如金额字段必须用DECIMAL而不是float/double这是财务系统的底线比如报销审批状态必须用数字状态机而不是随便写个字符串比如页面的每一次新增、修改操作之后要刷新列表数据避免出现刚提交的数据看不到的尴尬。这些细节在论文里写上一段“系统非功能需求分析”比空谈“系统稳定、界面美观”要有说服力得多。1.3 技术栈、目录结构与开发环境技术选型固定下来之后先把开发环境列清楚避免后期环境问题干扰开发进度。我推荐用JDK 8 Tomcat 8.5 MySQL 5.7的组合原因很简单这三个版本都是十几年生产环境验证过的老搭档教程多、坑少根本不需要折腾版本兼容。IDE方面虽然Eclipse是很多教材里的老面孔但IDEA对JSP项目的支持更顺手新版本IDEA创建Java Enterprise项目时选Web Application模板配置好Tomcat就能直接跑。目录结构我用的是传统包分层com.school.finance.servlet存放Servlet控制器com.school.finance.dao存放数据库操作类com.school.finance.model存放实体类com.school.finance.util放工具类数据库连接、MD5加密、Excel导出等Web根目录下按模块建子文件夹放JSP页面。这里多说一句不要把JSP页面全堆在根目录否则包结构再漂亮页面混乱照样会被导师批评。我当时是按admin后台管理、billing缴费、expense报销、common公共页面分目录放的后面改样式找文件非常快。2. 数据库建模与后端实现——把“钱”管好2.1 核心表设计与字段约束数据库是财务系统的地基表设计得好不好直接决定后面写代码是享受还是受刑。我最终设计了7张核心表用户表、角色表、缴费项目表、缴费记录表、报销单表、预算科目表、操作日志表。这个量级对于一个毕业设计来说不多不少既能说明设计思路又不至于让建表和初始化数据变成负担。以缴费记录表为例字段设计要涵盖业务核心和外联信息。主键id用自增intstudent_no记录学号item_id关联缴费项目表amount字段必须用DECIMAL(10,2)这是财务系统的基本素养pay_time记录缴费时间operator记录操作人status标记状态0未支付1已支付2已退费。特别提醒数据库字段命名一定不要用中文也尽量别直接用type、name这种单词做字段名太容易和关键字冲突表名前缀用billing_pay_record或t_pay_record都行关键是全库统一。金额精度问题我单独拎出来说。float和double在计算机里是二进制浮点数0.1 0.2算出来是0.30000000000000004这在普通管理系统里无所谓在财务系统里就是事故。Java里用BigDecimal配合数据库DECIMAL虽然操作麻烦一点但这是标准做法。另一个容易忽略的是“软删除”概念真实业务里缴费记录不能随便物理删除要留痕。我实际开发时给业务表都加了is_deleted字段默认0删除操作实际是UPDATE成1这样报表统计时统一过滤掉逻辑清晰也能在论文里作为亮点。2.2 JDBC连接池与DAO层构建传统JSP项目最基础的数据访问方案是JDBC写到后面我强烈建议引入Druid或者C3P0连接池。道理很简单你的系统每次请求都要查数据库如果每次都重新建立物理连接数据库很快会被拖垮。用连接池相当于提前准备了多个数据库会话放在池子里请求来了直接拿一个用用了再还回去性能和稳定性完全不是一回事。这里我直接给出一个基于Druid的工具类骨架换成其他连接池思路一样。public class DBUtil { private static DataSource dataSource; static { try { Properties props new Properties(); InputStream in DBUtil.class.getClassLoader().getResourceAsStream(druid.properties); props.load(in); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(ResultSet rs, PreparedStatement ps, Connection conn) { if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (ps ! null) { try { ps.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }DAO层我只用了一个抽象思路每张业务表对应一个Dao类类里只写这张表相关的增删改查方法。比如PayRecordDao有insert、selectByStudentNo、selectByDateRange、countByDate等ExpenseDao有insert、updateStatus、selectPendingList等。千万不要写一个万能Dao包打天下那样类名看着很炫酷但实际维护时找方法能找到哭。这里还要强调一个安全底线数据库操作永远用PreparedStatement不要用Statement拼字符串。防SQL注入不只是理论我测试时确实遇到过在搜索框输入一个单引号把整站搞崩的情况PreparedStatement能把这类问题从根上杜绝。2.3 缴费与报销的核心业务逻辑业务逻辑里最值得写进论文的就是“事务控制”。举例来说学生缴费成功时要做两件事往缴费记录表插一条记录同时更新缴费项目的已收金额。这两个操作必须同时成功或同时失败。如果先插记录、更新金额那一步出错数据库里就会多了一笔“幽灵缴费”对账的时候非常难受。解决办法是用Connection事务关闭自动提交执行两条SQL都成功才commit任何异常就rollback并回滚连接状态。public boolean addPayRecord(PayRecord record) { Connection conn null; PreparedStatement psInsert null; PreparedStatement psUpdate null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); String insertSql INSERT INTO billing_pay_record(...) VALUES(?,?,?,?,?,?); psInsert conn.prepareStatement(insertSql); // ... 设置参数 psInsert.executeUpdate(); String updateSql UPDATE billing_item SET received_amount received_amount ? WHERE id ?; psUpdate conn.prepareStatement(updateSql); // ... 设置参数 psUpdate.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ignored) {} } e.printStackTrace(); return false; } finally { DBUtil.close(null, psInsert, null); DBUtil.close(null, psUpdate, conn); } }报销模块的另一个重点是“状态机”。我定义了0草稿、1待审批、2已通过、3已驳回四个状态页面上所有按钮的显示都根据状态判断只有草稿状态能编辑删除只有待审批状态能通过或驳回已通过状态不允许撤销。这样一个简单的状态机就把业务规则固化下来了不会出现“已驳回的单子还能被审核”这种逻辑漏洞。这部分在论文里画一个状态转换表评审老师一看就知道你动过脑子。2.4 统计报表与Excel导出的实用方案统计功能是整个系统最容易出彩的地方。我做了两个维度的统计按日期统计每日缴费金额和笔数按部门/学院统计报销金额。SQL基本就是GROUP BY SUM DATE_FORMAT的组合难度不大但很实用。特别提醒这种统计SQL的核心是“范围条件”要准确用PreparedStatement传起止日期时日期边界很容易出错比如你想查11月1日到11月30日结果只有到29日的数据大概率是结束日期没拼上23:59:59或者用了错误的日期格式化函数。我当时在MySQL里统一用DATE(pay_time) ? AND DATE(pay_time) ?两侧都是字符串日期避免时间戳精度干扰。导出Excel我用的是Apache POI这也是Java导出技术的标准答案。给一个小建议如果只是导出几十条数据直接Workbook生成XLSX没问题但如果你导出了上万条统计明细一定分页查询并分批写入Excel否则JVM内存很快就撑不住。另外导出文件的响应头里Content-Type要用application/vnd.ms-excel文件名要用URLEncoder编码否则浏览器下载中文文件名直接乱码。3. 前端页面与会话控制——JSP没有想象中落伍3.1 页面整体布局与公共组件复用JSP页面最容易写成一堆互相重复的HTML改一个导航栏得把所有页面都翻一遍。我接手过很多这样的“祖传代码”深受其害所以自己做项目时坚决用 jsp:include 把公共部分抽出来。具体做法是建一个common目录存放header.jsp和footer.jspheader里放导航菜单footer里放版权信息和全局JS每个业务页面内只用include标签引入以后再改菜单就是改一个文件的事。这套做法配合JSTL的c:if和c:forEach页面逻辑比单纯嵌Java脚本片段干净得多也更容易过查重。如果你想让页面看起来不像2005年的古董另一个快速见效的办法是引入一个轻量级CSS框架。我试过Bootstrap也试过只引入Element UI的图标库。对于这种传统项目Bootstrap表格、按钮、表单控件直接拉高整体质感不需要自己手写复杂的样式。实际开发时我直接在公共header里引用了Bootstrap的CDN和Element UI的图标库花了一个下午把所有新增、编辑、删除按钮统一换成带图标的按钮页面观感立刻不同。3.2 登录认证、验证码与Session会话管理登录功能看似简单却是答辩时最容易问倒人的点。不要只做一个登录成功的页面跳转要做到“登录会话不可绕过”。我的实现路径是用户输入账号密码和验证码 → 密码先盐值MD5加密 → 查询用户表和角色表 → 匹配成功则把用户对象和角色标识存入Session → 跳转到对应角色的首页。同时写一个LoginFilter拦截器把它配置成拦截所有“/admin/”和“/billing/”等受保护资源未登录请求直接重定向到登录页。public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); Object user (session null) ? null : session.getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); } else { chain.doFilter(req, resp); } } }这段代码非常简单但它解释了Web开发里最核心的“会话保持”概念。答辩老师通常会顺着往下问Session存在哪里默认失效时间多久Cookie和Session的关系是什么这些你都得提前准备。另一个细节是退出登录一定要调用session.invalidate()只把登录状态改成未登录而不销毁会话会有安全隐患。权限控制方面我用了角色字段做粗粒度拦截管理员和财务人员在页面上看到菜单不同普通用户只能操作自己的数据。如果要做细粒度权限可以再引入一个权限表但对毕设来说角色控制已经足够体现工作量。3.3 “页面加载完刷新一次”的前端小技巧热词里有一条“jsp页面让加载完后刷新一次”这个需求我太有共鸣了。最常见的场景是登录成功后跳转到主页或者提交完表单后回到列表页结果页面虽然跳过来了但数据还是旧的非得手动F5一次不可。这个问题的根源在于浏览器缓存了页面快照或者你的跳转方式是forward而不是redirect。传统JSP项目里表单提交后最标准的做法是“提交后重定向”也就是PRG模式Post/Redirect/Get。Servlet处理完业务不要直接forward到JSP因为forward是同一次请求内跳转用户按F5就会重复提交正确做法是用sendRedirect重定向到列表Servlet由列表Servlet重新查询数据库并渲染页面。这样页面每次都会重新加载数据自然是最新的。// Servlet中处理完业务 resp.sendRedirect(request.getContextPath() /payRecord/list);如果你确实遇到了“跳转之后需要强制刷新一次”的场景还有一种土办法在JSP的 标签里加onload事件调用location.reload()。但这里要慎重如果把reload用在列表页用户点一次翻页可能触发无限刷新我早期调试时就被这个坑折磨过。最终的判断标准是尽量从后端跳转逻辑上解决不要在页面上刷小聪明。3.4 个人信息展示页面的JSTL写法“jsp个人信息展示页面”也是搜索热词我这里给一个通用示例用它来展示当前登录用户的基本信息和角色菜单。整套写法非常简单Servlet从Session里取出用户的Bean对象setAttribute(user, user)JSP页面用EL表达式${user.name}取姓名${user.roleName}取角色名称配合JSTL的c:if test${user.roleId 1}来动态显示管理员专属菜单。用这套组合的好处是页面里几乎没有Java代码全是HTML标签加JSTL标签结构一目了然也方便美工直接套样式。除了静态信息展示这个页面还可以加上“修改密码”功能也可以放操作日志。因为用户修改密码也是安全系统中的常见考点后端校验旧密码、新密码二次确认、加密存储新密码三步走完又是一个小的业务闭环。我在个人中心里放进最近10条操作日志之后整个系统“留痕”的概念就立住了答辩时讲安全设计也有素材。4. 部署打包与高频问题排查4.1 传统JSP项目打War包的完整流程“传统jsp项目打包war”这个需求八成是在部署环节卡住了。我用IDEA给你拆解一遍。首先保证项目能通过Maven或本地Tomcat正常访问。然后打开Project StructureCtrlAltShiftS选择Artifacts点加号选Web Application: ArchiveName改成项目名Output directory选到桌面这种好找的位置在Output Layout里确保已经把“所有项目编译产物和依赖库”都加进去了特别是JDBC驱动和Druid连接池的jar包缺了任何一个部署后都会ClassNotFoundException。最后点Build → Build Artifacts → Rebuild就能生成xxx.war。拿到war包后部署方式有两种。一种是把war直接扔到Tomcat的webapps目录启动Tomcat时它会自动解压并部署另一种是在conf/server.xml里配置虚拟目录把war包指向外部路径。第一种最简单适合毕设演示。这里有一个很多人不知道的小坑war包解压出来的目录名就是访问路径。比如你打包成finance.war访问地址就是http://localhost:8080/finance/。如果你的JSP里有引用相对路径的资源比如css/style.css那一定要改成${pageContext.request.contextPath}才能稳定访问。数据库部署方面建议用Navicat或者命令行执行建表SQL单独准备一个init.sql文件包含全部建表语句和初始管理员账号数据。这个习惯不仅方便你自己部署也是论文附录里的加分项。4.2 高频问题排查速查表项目做完整之后经常会遇到一些看起来离奇的问题我把实战中见过和踩过的高频问题整理成了一张速查表照着排查能省下大半天的调试时间现象可能原因排查与解决办法项目启动后访问报404上下文路径不对或war包没解压成功确认访问路径与war包名一致检查Tomcat的logs/catalina.outJSP页面中文显示乱码页面编码、请求编码、数据库编码不一致JSP头设置UTF-8Servlet设置request.setCharacterEncoding连接URL加useUnicodetruecharacterEncodingUTF-8登录请求500后台有SQL异常数据表字段与DAO的ResultSet映射不一致检查getString/getInt方法对应的列名是否与数据库一致表单重复提交产生重复缴费记录没有使用PRG模式forward后就刷新改为sendRedirect关键操作考虑加令牌机制访问受保护页面跳回登录页死循环Filter拦截路径包含登录页本身在doFilter中放行/login.jsp和验证码Servlet或使用exclude路径配置部署后提示找不到JDBC驱动war包没有包含mysql-connector依赖确认Artifacts Output Layout中包含所有依赖jar包页面样式全部丢失前端资源路径写成了绝对路径统一使用${pageContext.request.contextPath}拼接css/js路径系统长时间运行后响应变慢数据库连接没有释放检查DAO层的Connection/Statement是否在finally中关闭引入连接池后确认归还逻辑4.3 答辩加分项与项目演进思路如果你还有余力让系统“更像样”我建议按优先级加这四个功能。第一是登录验证码用Java绘制一个简单的图形验证码存到Session拦截器里对比这个功能代码量不算大但演示时非常有说服力。第二是操作日志留痕把登录、缴费登记、报销审核、导出操作统一记录下来展示的时候调出来给评委看比任何文字介绍都直观。第三是图表化统计引入一个纯前端的图表库比如ECharts把月度收支情况用折线图呈现系统会立刻上了一个档次。第四是审批流模块的细化比如报销金额超过5000元需要二级审批用一个approve_level字段就能实现这是业务深度的体现。从项目演进的角度看你还可以在论文“总结与展望”部分写清楚这套系统如果要落地到生产环境有哪些改进方向。比如引入Spring Security做权限框架用MyBatis简化数据层或者把JSP改成前后端分离架构用Vue RESTful API重新实现。这些方向不需要你现在做出来但证明你有行业视野是非常好的软性加分。最后说一点个人体会我做过很多项目但每次有人问我“JSP还有没有价值”时我还是会把这个问题拆成两层回答。从就业角度看新项目确实没多少人用JSP了但没有必要因此否定一个技术的学习价值从毕业设计角度看JSP项目让你逼着自己理解Web应用的完整骨架从请求到响应、从Session到过滤器、从数据库表到SQL优化这些底层能力在任何框架下都通用。我当年踩着那些乱码和404的坑爬出来之后后来学Spring Boot、学微服务都顺利得多因为底子打扎实了。如果你正在做这个题目我建议你少看一点“速成宝典”多花两个小时把建表SQL和Servlet生命周期亲手走一遍。把每一步都弄明白答辩时老师问什么你都不慌。祝你顺利通过。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →