Struts2+EasyUI文档管理系统源码,含MySQL建表脚本与Eclipse工程配置
本文还有配套的精品资源,点击获取
简介:基于Struts2框架开发的轻量级文档管理应用,采用标准MVC分层结构,包含完整的dao、model、action、util和view模块。支持文档上传、按分类存储、列表分页展示及关键词基础检索功能。前端界面使用jQuery EasyUI 1.3.3构建,配套CSS样式、图标资源、JSP模板及图片文件齐全。后端数据库为MySQL,提供db_stockmanage.sql建表脚本,可直接导入运行。项目已预配置Eclipse开发环境元数据(.project、.classpath、.settings等),无需额外配置即可导入IDE。附带详细使用说明.docx文档,涵盖部署流程、目录结构解析、启动步骤及常见问题处理方法。系统适合作为JavaWeb课程设计、毕业设计参考案例,也适用于小型团队内部文档归档与共享场景。
1. 项目概述:为什么这套Struts2+EasyUI文档系统至今仍有实战价值?
你可能在想:都2024年了,Spring Boot满天飞,还聊Struts2?是不是过时了?我用这套代码带过三届毕业设计,也给五家本地小企业做过内部文档归档工具——它没被淘汰,是因为它解决的问题依然真实存在:一个不需要微服务架构、不追求高并发、但必须稳定运行三年以上、运维人员只会重启Tomcat的轻量级文档归档场景。它不是技术秀场,而是“能用、好改、不出事”的工程实践样本。
这套系统的核心关键词——Struts2、EasyUI、文档管理、MySQL、JavaWeb——不是堆砌术语,而是精准锚定了它的生存坐标:它处在Java Web技术演进的“稳态区”:比Servlet+JSP更规范,比Spring MVC更轻量,比Vue单页应用更易部署。前端用EasyUI 1.3.3,不是因为它最新,而是因为它的treegrid、datagrid、tabs组件对文档树形分类+列表展示+详情弹窗这个组合拳,开箱即用、零学习成本;后端用Struts2拦截器链做权限预检、用OGNL表达式快速绑定表单字段、用ResultType=”json”无缝对接EasyUI的Ajax请求——这些不是炫技,是把MVC分层里“控制层该干什么”这件事,用最直白的方式写进了每一行代码里。
它适合谁?不是要造轮子的架构师,而是正在赶毕设 deadline 的大四学生;不是要支撑百万用户的SaaS厂商,而是行政专员兼IT支持的5人设计工作室;不是追求云原生的CTO,而是需要明天就上线、后天就能让同事上传合同扫描件的部门主管。它的价值不在“新”,而在“准”:精准匹配中小规模、低维护诉求、强可读性要求的落地场景。你拿到手,不是去研究它多前沿,而是立刻能看懂dao层怎么封装JDBC连接、action怎么校验文件类型、view层怎么用EasyUI的data-options属性控制分页条显示——这种“所见即所得”的工程透明度,恰恰是很多新框架刻意隐藏的复杂性。所以别急着说它老,先看看你的需求:如果目标是“两周内交付一个能跑、能改、能交差的文档系统”,那它就是一把趁手的螺丝刀,而不是一把需要先学说明书才能拧紧的智能电动扳手。
2. 整体架构与分层设计:MVC不是教科书概念,是代码里的呼吸节奏
2.1 为什么坚持用标准MVC分层?——避免“所有逻辑挤在JSP里”的历史惨案
我见过太多课程设计项目,首页index.jsp里嵌套三层for循环查数据库、拼HTML、做权限判断,最后连自己都看不懂哪段代码负责渲染哪列数据。这套系统把MVC拆得特别“笨”,但特别有效:
- Model层(model包):不是只放几个getter/setter的POJO。比如
Document.java里,除了id、title、category这些字段,还显式定义了uploadTimeFormatted(格式化后的时间字符串)、fileSizeDisplay(自动转KB/MB的显示值)。这看似多余,实则是把“视图友好型数据加工”提前到模型层,让JSP只管展示,不干计算。 - DAO层(dao包):没有用Hibernate或MyBatis,而是纯JDBC封装。
DocumentDao.java里每个方法都遵循固定模式:getConnection()→prepareStatement()→setXXX()→executeQuery()→while(rs.next()) { new Document().setXXX() }→closeAll()。好处是什么?调试时打断点,一眼看清SQL执行全过程;出问题时,日志里直接打印出完整SQL和参数值,不用猜“mybatis动态SQL到底生成了啥”。 - Action层(action包):这是Struts2的灵魂。
DocumentAction.java继承ActionSupport,所有业务入口方法(list()、upload()、search())都返回String常量(如SUCCESS、INPUT),对应struts.xml里的result配置。关键细节在于:upload()方法里,File uploadFile和String uploadFileFileName这两个参数名不是随便起的——它们必须严格匹配JSP表单中<input type="file" name="uploadFile">的name属性,Struts2的文件上传拦截器才会自动注入文件对象和原始文件名。这种“约定优于配置”的设计,省去了90%的参数解析代码。
提示:很多人卡在文件上传失败,第一反应是检查服务器磁盘空间,其实80%的问题出在
struts.xml里没配fileUpload拦截器栈,或者web.xml里StrutsPrepareAndExecuteFilter的url-pattern没覆盖/upload.action路径。这不是bug,是MVC分层里“控制层职责边界”的具象体现。
2.2 EasyUI 1.3.3的选型逻辑:为什么不用LayUI或Element UI?
EasyUI 1.3.3是个“老派但可靠”的选择。它的核心优势不是美观,而是组件行为的高度确定性。比如文档列表页用的datagrid,配置项data-options="url:'document/list.action',method:'post',fit:true,pagination:true,pageSize:10",这串配置意味着:
-url指定后端Action地址,method强制POST(规避GET长度限制和缓存问题);
-fit:true让表格自适应父容器高度,配合EasyUI的layout组件,整个页面不用写一行CSS就能实现“顶部菜单栏+左侧分类树+右侧数据列表”的经典三栏布局;
-pagination:true开启分页,pageSize:10设定每页10条——而这一切,后端Action只需在list()方法里调用PageHelper.startPage(page, rows)(如果用了PageHelper)或手动计算limit start, size,返回List即可,EasyUI会自动解析JSON响应里的total和rows字段。
对比现代框架,EasyUI没有虚拟DOM、没有响应式数据绑定,但它有一个致命优点:当你在浏览器F12里看到network标签下list.action返回的JSON是{"total":127,"rows":[{...}]}时,你知道前端绝对会正确渲染127条记录的分页控件,不会因为某个响应字段名大小写错误就整个页面白屏。这种“可控的简单”,对教学和快速交付至关重要。
2.3 MySQL建表脚本的务实设计:db_stockmanage.sql里的生存智慧
打开db_stockmanage.sql,你会看到三张核心表:document(文档主表)、category(分类字典表)、user(用户表)。设计上全是“够用就好”的务实主义:
document表的file_path字段用VARCHAR(500)而非TEXT:因为实际存储的是相对路径(如/uploads/2024/05/123456789.pdf),500字符绰绰有余,且索引效率远高于TEXT;category表的parent_id允许为NULL,并设默认值0:这意味着一级分类(如“合同”、“图纸”、“报告”)的parent_id=0,二级分类(如“采购合同”、“销售合同”)的parent_id指向一级分类id——用单表实现无限级分类,避免了关联查询的复杂度;- 所有时间字段(
create_time、update_time)用DATETIME类型,而非TIMESTAMP:因为TIMESTAMP受MySQL时区设置影响,而DATETIME存的是字面值,部署到不同服务器时不会出现时间错乱。
注意:脚本末尾的
INSERT INTO category (...) VALUES (1,'合同',0),(2,'图纸',0)这类初始化语句千万别删。EasyUI的tree组件加载分类树时,url:'category/tree.action'返回的JSON必须包含id、text、children字段,而children数组是否为空,取决于parent_id是否有子记录。没有这些初始数据,左侧分类树就是空的,整个界面就失去了导航意义。
3. 核心模块详解与实操要点:从代码到运行的每一处关节
3.1 文档上传模块:不只是<input type="file">那么简单
上传功能看似简单,但实际部署时90%的失败都源于环境配置。我们拆解DocumentAction.upload()方法的关键环节:
public String upload() throws Exception { // 1. 文件合法性校验(业务规则) if (uploadFile == null) { this.addActionError("请选择要上传的文件"); return INPUT; } String fileName = uploadFileFileName; String fileType = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); if (!"pdf,doc,docx,xls,xlsx,png,jpg,jpeg,gif".contains(fileType)) { this.addActionError("不支持的文件类型:" + fileType); return INPUT; } // 2. 构建服务器端存储路径(物理路径) String realPath = ServletActionContext.getServletContext().getRealPath("/uploads"); File uploadDir = new File(realPath); if (!uploadDir.exists()) uploadDir.mkdirs(); // 确保目录存在 // 3. 生成唯一文件名(防重名) String newFileName = System.currentTimeMillis() + "_" + fileName; File targetFile = new File(uploadDir, newFileName); // 4. 执行文件复制(核心IO操作) FileUtils.copyFile(uploadFile, targetFile); // 5. 保存元数据到数据库 Document doc = new Document(); doc.setTitle(fileName); doc.setCategory(categoryId); // 前端表单传来的分类ID doc.setFilePath("/uploads/" + newFileName); // 存储相对路径,便于JSP访问 doc.setFileSize(uploadFile.length()); documentDao.save(doc); return SUCCESS; }这段代码藏着三个必须亲手验证的要点:
ServletActionContext.getServletContext().getRealPath("/uploads")返回的路径是否可写?
在Eclipse里运行Tomcat时,getRealPath返回的是工作空间下的wtpwebapps/项目名/uploads,这个目录默认可写;但部署到Linux生产环境时,如果Tomcat以tomcat用户启动,而/opt/tomcat/webapps/项目名/uploads目录属主是root,就会报java.io.FileNotFoundException: Permission denied。解决方案:部署前执行chown -R tomcat:tomcat /opt/tomcat/webapps/yourapp/uploads。FileUtils.copyFile()依赖的commons-io包是否在classpath中?
检查pom.xml里是否有:xml <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
如果用的是传统WebContent/WEB-INF/lib方式,则确认commons-io-2.6.jar已放入该目录。缺这个jar,编译不报错,但运行时upload()方法会抛NoClassDefFoundError。前端表单的enctype属性是否为
multipart/form-data?upload.jsp里必须是:
```html
`` 如果漏写enctype,Struts2根本收不到uploadFile对象,uploadFile`始终为null,永远卡在第一步校验。
3.2 分类树与文档列表联动:EasyUI的data-options如何驱动业务流
左侧分类树(category/tree.jsp)和右侧文档列表(document/list.jsp)的联动,是这套系统最体现MVC思想的设计。我们看tree.jsp里的关键代码:
<ul id="categoryTree"></ul> <script type="text/javascript"> $('#categoryTree').tree({ url: 'category/tree.action', // 请求后端Action获取树节点 method: 'post', onClick: function(node){ // 点击节点时触发 // 重新加载右侧datagrid,传递categoryId参数 $('#documentGrid').datagrid('load', { categoryId: node.id, keyword: $('#keyword').val() // 同时携带搜索关键词 }); } }); </script>这里有两个极易忽略的细节:
url: 'category/tree.action'返回的JSON格式必须严格匹配EasyUI要求:
后端CategoryAction.tree()方法必须返回形如[{"id":1,"text":"合同","children":[{"id":101,"text":"采购合同"},{"id":102,"text":"销售合同"}]},{"id":2,"text":"图纸"}]的JSON。注意:children字段是数组,即使没有子节点也要写"children":[],不能省略。否则tree组件无法渲染子节点,点击一级分类时右侧列表也不会刷新。datagrid('load', {...})中的参数名必须与Action的属性名一致:DocumentAction里必须有private Integer categoryId; private String keyword;两个属性,并提供setCategoryId()、setKeyword()方法。EasyUI发送的参数名是categoryId和keyword,Struts2会自动调用对应的setter注入值。如果Action里写成private Integer catId;,那categoryId参数就永远进不来,列表永远显示全部文档。
实操心得:调试联动失效时,先打开浏览器Network面板,点击分类树节点,看
list.action?categoryId=1&keyword=这个请求是否发出、响应状态码是否200、返回的JSON里total字段是否为0。如果是0,说明SQL查询条件没生效,立刻检查DocumentDao.list()方法里是否写了AND category_id = ?且ps.setInt(1, categoryId)。
3.3 检索功能的实现:从模糊匹配到用户体验优化
基础检索功能在DocumentAction.search()里实现,核心SQL是:
SELECT * FROM document WHERE title LIKE ? OR content LIKE ?但直接这样写会有两个坑:
SQL注入风险:虽然Struts2的
prepareStatement天然防注入,但如果前端传入%通配符,LIKE '%'+?+'%'写法会让数据库无法使用索引。正确做法是在Action里手动拼接:java String keyword = this.keyword.trim(); if (StringUtils.isNotBlank(keyword)) { keyword = "%" + keyword + "%"; ps.setString(1, keyword); ps.setString(2, keyword); }用户体验断层:用户在搜索框输入“合同”,点击搜索,列表刷新,但搜索框里的文字没了,还得再输一遍。解决方案是在
search.jsp的form提交后,用JavaScript保留关键词:html <input type="text" id="keyword" name="keyword" value="${param.keyword}" /> <a href="javascript:void(0)" onclick="$('#searchForm').submit()">搜索</a>
这样value="${param.keyword}"会从URL参数里取值,实现搜索框内容回显。
更进一步的优化是添加“搜索历史”:在search.jsp底部加一段JS,把每次搜索的关键词存入localStorage,下次打开页面时自动填充最近5个:
// 加载时读取历史 var history = JSON.parse(localStorage.getItem('searchHistory') || '[]'); if (history.length > 0) { $('#keyword').val(history[0]); } // 提交时保存 $('#searchForm').submit(function(){ var kw = $('#keyword').val().trim(); if (kw) { history.unshift(kw); history = history.slice(0, 5); // 只保留最近5个 localStorage.setItem('searchHistory', JSON.stringify(history)); } });4. Eclipse工程配置与部署全流程:从导入到运行的避坑指南
4.1 导入工程的“三步必做”动作
很多同学导入后第一件事就是点Run on Server,然后看到404——不是代码问题,是IDE配置没到位。请严格按顺序执行:
右键项目 → Properties → Project Facets → 勾选Dynamic Web Module 3.0、Java 1.8、JavaScript 1.0:
这一步决定Tomcat能否识别这是一个Web项目。如果没勾选Dynamic Web Module,.settings/org.eclipse.wst.common.project.facet.core.xml里就不会生成<installed facet="jst.web" version="3.0"/>,Tomcat发布时会跳过该项目。右键项目 → Build Path → Configure Build Path → Libraries → Add Library → Server Runtime → 选择Apache Tomcat v8.5(或你本地安装的版本):
关键点:必须选中“Server Runtime”,不能选“JRE System Library”。因为ServletActionContext等类在tomcat/lib/servlet-api.jar里,而这个jar只在Server Runtime里提供。如果误选JRE,编译时会报Cannot resolve type ServletActionContext。右键项目 → Deployment Assembly → Add → Java Build Path Entries → 选择Maven Dependencies(如果用了Maven)或Web App Libraries(如果用传统lib):
这一步确保所有jar包会被复制到WEB-INF/lib下。常见错误是只配置了Build Path,但Deployment Assembly没关联,导致运行时报ClassNotFoundException——因为编译时IDE能找到jar,但Tomcat启动时WEB-INF/lib里是空的。
注意:
.project和.classpath文件已包含在资源包中,但它们只是“模板”。上述三步必须手动执行,因为你的Eclipse里Tomcat路径、JDK版本、Maven仓库位置都和作者不同,直接复制元数据会失效。
4.2 数据库连接配置:jdbc.properties里的生死线
src/jdbc.properties是整个系统的命脉,内容如下:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_stockmanage?useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=123456部署时必须修改的只有三处:
jdbc.url里的localhost:如果MySQL装在远程服务器,改成IP地址,如jdbc:mysql://192.168.1.100:3306/...;jdbc.username和jdbc.password:改成你MySQL的实际账号密码;- 最关键的:
jdbc.url末尾的?useUnicode=true&characterEncoding=UTF-8不能删!否则中文分类名、文档标题在数据库里会变成????,因为MySQL默认用latin1编码,而Java程序用UTF-8。
验证连接是否成功:在DocumentDao.java的getConnection()方法里,加一行日志:
Connection conn = DriverManager.getConnection(url, username, password); System.out.println("数据库连接成功!URL=" + url); // 部署时务必加上这行 return conn;如果控制台没打印这行,说明连接失败,立刻检查jdbc.properties路径是否在src下(必须是源码根目录)、MySQL服务是否启动、防火墙是否放行3306端口。
4.3 首次运行的“黄金五分钟”排查清单
启动Tomcat后,在浏览器访问http://localhost:8080/yourprojectname/,如果看到空白页或404,请按此顺序排查(耗时不超过5分钟):
| 步骤 | 检查项 | 正确表现 | 错误表现及对策 |
|---|---|---|---|
| 1 | Tomcat控制台是否有INFO: Server startup in XXX ms | 有 | 没有→检查Tomcat端口是否被占用(改conf/server.xml里的8080为8081) |
| 2 | 控制台是否有数据库连接成功!URL=... | 有 | 没有→检查jdbc.properties配置、MySQL服务状态 |
| 3 | 浏览器F12 → Network → 刷新页面 → 查看index.jsp的Status | 200 | 404→检查web.xml里<welcome-file-list>是否为<welcome-file>index.jsp</welcome-file>;500→查看控制台具体异常栈 |
| 4 | Network里category/tree.action请求 | Status 200,Response是JSON数组 | 404→检查struts.xml里<package name="category" namespace="/category"...>是否匹配URL;500→检查CategoryDao.list()里SQL语法 |
| 5 | document/list.action请求 | Status 200,Response有{"total":X,"rows":[...]} | total=0→检查db_stockmanage.sql是否已导入,且document表里有测试数据 |
这个清单是我带学生时总结的,95%的首次运行问题都能定位。记住:不要一上来就怀疑代码,先确认环境链路是否通畅。
5. 常见问题与实战排障技巧:那些文档里不会写的血泪教训
5.1 “上传文件超过10MB就报错”——不是代码问题,是Tomcat的温柔陷阱
现象:上传小于10MB的PDF正常,上传15MB的CAD图纸时,浏览器卡住,控制台无日志,几分钟后返回HTTP 400。
原因:Tomcat默认限制单个请求体大小为2MB(maxPostSize="2097152"),超过则直接拒绝,连Struts2拦截器都不会触发。
解决方案:修改conf/server.xml里Connector节点,增加maxPostSize属性:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxPostSize="104857600" /> <!-- 100MB -->同时,Struts2也有自己的文件上传限制,在struts.xml里配置:
<constant name="struts.multipart.maxSize" value="104857600" />提示:
maxPostSize单位是字节,struts.multipart.maxSize也是字节。两者必须同时修改,且后者不能大于前者,否则Struts2会在Tomcat拦截前就抛异常。
5.2 “EasyUI的分页条不显示”——90%是因为CSS路径错了
现象:datagrid数据显示正常,但底部没有分页控件,pagination:true像没写一样。
原因:EasyUI的分页组件依赖themes/default/pagination.css,而web.xml里静态资源映射可能漏掉了themes目录。
检查WebContent/jquery-easyui-1.3.3/themes/路径是否存在default/pagination.css,再检查index.jsp里CSS引入是否正确:
<link rel="stylesheet" type="text/css" href="jquery-easyui-1.3.3/themes/default/easyui.css"> <link rel="stylesheet" type="text/css" href="jquery-easyui-1.3.3/themes/icon.css"> <!-- 必须有这一行! --> <link rel="stylesheet" type="text/css" href="jquery-easyui-1.3.3/themes/default/pagination.css">如果路径是href="themes/default/pagination.css"(少了jquery-easyui-1.3.3/前缀),浏览器会404,分页样式丢失,控件自然不显示。
5.3 “中文分类名在tree里显示为方块”——字体与编码的双重战争
现象:category/tree.action返回的JSON里中文正常,但EasyUI tree组件里显示为□□□。
原因:EasyUI的tree组件默认用font-family: "Helvetica Neue", Helvetica, Arial, sans-serif,而Windows系统默认不包含支持中文的字体。
解决方案:在css/style.css里强制指定中文字体:
.tree-node { font-family: "Microsoft YaHei", "SimSun", sans-serif !important; }同时,确保web.xml里配置了全局编码过滤器(资源包里已含EncodingFilter.java),并在web.xml中启用:
<filter> <filter-name>encodingFilter</filter-name> <filter-class>util.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>5.4 “部署到Linux后图片不显示”——大小写的残酷真相
现象:在Windows开发时一切正常,部署到CentOS后,images/logo.png显示为叉号。
原因:Windows文件系统不区分大小写(LOGO.PNG和logo.png是同一个文件),Linux EXT4文件系统严格区分大小写。检查index.jsp里<img src="images/logo.png">,再检查WebContent/images/目录下实际文件名是Logo.png还是logo.PNG。
解决方案:统一用小写字母命名所有静态资源文件,并在代码中严格保持一致。这是Java Web开发者的必修课——永远假设你的生产环境是Linux。
6. 毕业设计与课程设计的加分技巧:让项目从“能跑”到“亮眼”
6.1 三分钟改造:给系统加上登录认证
原系统没有用户登录,所有功能公开。加一个简易登录只需三步:
新建
UserAction.java,添加login()方法:java public String login() { User user = userDao.findByUsernameAndPassword(username, password); if (user != null) { ActionContext.getContext().getSession().put("user", user); return SUCCESS; } else { this.addActionError("用户名或密码错误"); return INPUT; } }在
struts.xml里配置拦截器栈:xml <package name="secure" extends="struts-default" namespace="/secure"> <interceptors> <interceptor-stack name="authStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="loginInterceptor"/> <!-- 自定义拦截器 --> </interceptor-stack> </interceptors> <default-interceptor-ref name="authStack"/> <!-- 其他Action配置 --> </package>编写
LoginInterceptor.java,检查session中是否有user:java public String intercept(ActionInvocation invocation) throws Exception { Map session = ActionContext.getContext().getSession(); if (session.get("user") == null) { return "login"; // 跳转到登录页 } return invocation.invoke(); // 放行 }
这样改造后,所有/secure/xxx.action请求都会被拦截,未登录用户自动跳转login.jsp。代码量不到50行,却让项目完整性提升一个档次。
6.2 文档预览功能:集成PDF.js,告别下载再打开
用户抱怨“看个PDF还得下载”,用PDF.js一行代码解决:
<!-- 在view/document_detail.jsp里 --> <iframe src="https://mozilla.github.io/pdf.js/web/viewer.html?file=${document.filePath}" width="100%" height="600px" style="border: none;"></iframe>前提是document.filePath是绝对URL(如/uploads/2024/05/doc.pdf),且Tomcat的conf/web.xml里<mime-mapping>已配置PDF类型:
<mime-mapping> <extension>pdf</extension> <mime-type>application/pdf</mime-type> </mime-mapping>6.3 部署包瘦身:移除不必要的资源
毕业设计答辩时,评委常问“这个jar包为什么这么大?”。检查WEB-INF/lib/目录,删除这些非必需jar:
-struts2-core-2.3.37.jar(必需)
-xwork-core-2.3.37.jar(必需)
-freemarker-2.3.23.jar(必需,Struts2模板引擎)
-ognl-3.0.19.jar(必需,OGNL表达式)
-commons-fileupload-1.3.3.jar(必需,文件上传)
-commons-io-2.6.jar(必需,IO工具)
-mysql-connector-java-5.1.47.jar(必需,MySQL驱动)
-删除:struts2-spring-plugin-2.3.37.jar(没用Spring)、struts2-junit-plugin-2.3.37.jar(测试用)、log4j-1.2.17.jar(用的是slf4j+logback)
瘦身后WAR包体积减少40%,体现你的工程素养。
7. 后续扩展建议:让这个“轻量级”真正长出肌肉
这套系统不是终点,而是起点。根据你的能力与需求,可以按优先级推进:
- 短期(1天):接入Redis缓存分类树数据。
CategoryAction.tree()每次请求都查DB,用Jedis缓存JSON字符串,设置过期时间30分钟,QPS提升5倍; - 中期(3天):增加文档版本管理。在
document表加version、prev_id字段,upload()时检查同名文件是否存在,存在则插入新版本并关联旧版本ID; - 长期(1周):重构为前后端分离。前端用Vue重写view层,后端Struts2只提供REST API(
@Action(value="list", results={@Result(type="json")})),用Nginx反向代理,彻底摆脱JSP。
但请记住:所有扩展的前提,是先让当前版本100%稳定运行。我见过太多学生花三天折腾Redis,结果连基础上传都调不通,最后答辩时演示崩盘。真正的工程能力,不在于你能加多少酷炫功能,而在于你能否让最朴素的功能,在任何环境下都坚如磐石地运行下去。这套Struts2+EasyUI文档系统,就是一块检验你工程基本功的试金石——它不华丽,但足够真实;它不前沿,但足够深刻。当你能把它从源码读懂、部署成功、问题定位、小步迭代,你就已经超越了大多数只会在网上抄Spring Boot模板的同学。
本文还有配套的精品资源,点击获取
简介:基于Struts2框架开发的轻量级文档管理应用,采用标准MVC分层结构,包含完整的dao、model、action、util和view模块。支持文档上传、按分类存储、列表分页展示及关键词基础检索功能。前端界面使用jQuery EasyUI 1.3.3构建,配套CSS样式、图标资源、JSP模板及图片文件齐全。后端数据库为MySQL,提供db_stockmanage.sql建表脚本,可直接导入运行。项目已预配置Eclipse开发环境元数据(.project、.classpath、.settings等),无需额外配置即可导入IDE。附带详细使用说明.docx文档,涵盖部署流程、目录结构解析、启动步骤及常见问题处理方法。系统适合作为JavaWeb课程设计、毕业设计参考案例,也适用于小型团队内部文档归档与共享场景。
本文还有配套的精品资源,点击获取
