简介这是一套完整的Java Web问卷调查系统实战项目面向Java初学者与SSM框架学习者聚焦Web应用开发全流程实践涵盖需求分析、前后端开发、数据库设计与部署上线等核心环节。资源包共847个文件含138个JS脚本实现前端交互与Layui组件调用、36个JSP页面构成用户端与管理端视图层、31个Java源码含SurveyController、QuestionService等关键业务类、35个JAR依赖及1个SQL建库脚本辅以CSS、XML配置与图片资源结构完整、模块清晰。压缩包大小为42.05MB采用RAR格式封装。已有1227人学习下载可直接导入IDEA等主流开发工具基于JDK 1.8TomcatMySQL 5.7环境快速运行配套完整权限体系管理员CRUD、问卷发布与统计报表与前后端分离式代码组织便于理解SSM整合原理与企业级问卷系统设计逻辑。1. 为什么一个“老派”技术栈的问卷系统反而在中小团队落地率最高你可能刚刷到过这类标题“Spring Boot Vue3 重构问卷系统”“基于微服务的高并发问卷平台”但现实是某高校教务处上线的学期评教系统、某本地政务服务中心的办事满意度采集模块、某连锁门店的员工培训反馈平台——它们背后跑着的是一套 Java SSMSpring SpringMVC MyBatis JSP Layui MySQL 的组合。不是因为团队守旧而是这套组合在需求明确、交付周期紧、运维资源有限、无高频并发压力的典型业务场景下具备极强的“确定性”开发路径清晰、调试链路短、部署包小、数据库结构直观、前端交互不炫但够用。它不追求技术新鲜度但能让你在两周内把“创建问卷→填写提交→后台查看统计”这条主干流程稳稳跑通且后续增删字段、改个导出逻辑、加个权限开关几乎不用动架构。如果你正接手一个需要快速上线、甲方不关心技术栈但要求“今天提需求明天能试用”的内部系统这套方案不是备选而是首选——它把“能用、好改、不出事”这三个工程师最朴素的诉求焊死在了每一层技术选型里。2. 搭建最小可运行骨架从零初始化 SSM JSP Layui 环境2.1 创建 Maven 工程并引入核心依赖pom.xml 关键片段我们不追求最新版而选经生产验证的稳定组合Spring 4.3.29.RELEASE兼容 JDK 8、MyBatis 3.4.6、JSP API 2.2Tomcat 8.x 原生支持。以下为pom.xml中必须声明的依赖块已剔除冲突版本与冗余插件properties spring.version4.3.29.RELEASE/spring.version mybatis.version3.4.6/mybatis.version mysql.version5.1.47/mysql.version /properties dependencies !-- Spring 核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- MyBatis 整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version1.3.2/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency !-- 数据库驱动与连接池 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version${mysql.version}/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.1.23/version /dependency !-- JSP 支持 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdjavax.servlet/groupId artifactIdservlet-api/artifactId version2.5/version scopeprovided/scope /dependency !-- Layui 静态资源不走 Maven后文说明 -- /dependencies逻辑说明servlet-api设为provided是因 Tomcat 容器已提供避免打包冲突druid替代c3p0或dbcp因其监控面板对排查连接泄漏极关键jstl是 JSP 中c:ifc:forEach等标签的基础不可省略。2.2 配置 Web 层web.xml SpringMVC DispatcherServletweb.xml是整个 MVC 的入口注册点必须严格按顺序声明监听器、过滤器、Servlet?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 加载 Spring 容器 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- 2. 编码过滤器必须在所有 Servlet 前 -- filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- 3. SpringMVC 前端控制器 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/springmvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping /web-app参数说明load-on-startup1确保 DispatcherServlet 在应用启动时即初始化避免首次请求延迟url-pattern//url-pattern表示接管所有请求静态资源需额外配置放行见 2.4CharacterEncodingFilter的forceEncodingtrue强制覆盖 request 的编码解决中文参数乱码。2.3 集成 Layui静态资源管理与基础页面结构Layui 不是 npm 包而是纯前端静态库。正确做法是将其解压后放入src/main/webapp/static/layui/目录非resources下并在 JSP 中通过绝对路径引用% page contentTypetext/html;charsetUTF-8 languagejava % !DOCTYPE html html head meta charsetutf-8 title问卷管理首页/title meta namerenderer contentwebkit meta http-equivX-UA-Compatible contentIEedge,chrome1 meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalable0 !-- Layui CSS -- link relstylesheet href${pageContext.request.contextPath}/static/layui/css/layui.css /head body div classlayui-layout layui-layout-admin !-- 头部 -- div classlayui-header div classlayui-logo问卷调查系统/div ul classlayui-nav layui-layout-right li classlayui-nav-itema href#管理员/a/li /ul /div !-- 左侧菜单 -- div classlayui-side layui-bg-black div classlayui-side-scroll ul classlayui-nav layui-nav-tree lay-filtertest li classlayui-nav-item layui-this a href${pageContext.request.contextPath}/questionnaire/listi classlayui-icon#xe63c;/i问卷列表/a /li li classlayui-nav-item a href${pageContext.request.contextPath}/questionnaire/createi classlayui-icon#xe654;/i新建问卷/a /li /ul /div /div !-- 主体内容区 -- div classlayui-body div stylepadding: 15px; blockquote classlayui-elem-quote欢迎使用问卷调查系统/blockquote /div /div /div !-- Layui JS -- script src${pageContext.request.contextPath}/static/layui/layui.js/script script // 初始化 Layui 模块 layui.use([element, layer], function(){ var element layui.element; var layer layui.layer; // 可在此添加全局 JS 逻辑 }); /script /body /html关键细节${pageContext.request.contextPath}是 JSP 内置对象动态获取应用上下文路径如/survey避免硬编码导致部署路径变更后资源 404Layui 的layui.js必须在所有自定义 JS 之前加载且layui.use()是模块化加载入口不可省略。2.4 静态资源放行让 CSS/JS/IMG 正常加载SpringMVC 默认会拦截所有请求包括/static/layui/css/xxx.css。必须在springmvc.xml中显式放行!-- springmvc.xml -- mvc:annotation-driven / !-- 放行静态资源匹配 /static/** 路径的所有请求直接由容器返回文件 -- mvc:resources mapping/static/** location/static/ / !-- 同时放行 favicon.ico避免控制台报错 -- mvc:resources mapping/favicon.ico location/favicon.ico /原理说明mvc:resources会注册一个ResourceHttpRequestHandler它绕过 Controller 执行链直接读取webapp/static/下的文件并写入响应流。若未配置浏览器控制台将出现大量 404Layui 样式完全失效——这是新手最常翻车的第一步。3. 数据库设计与 MyBatis 映射从问卷到题目的三层关系建模3.1 MySQL 表结构设计含外键与索引问卷系统核心实体为questionnaire问卷主表、question题目表、option选项表。采用物理外键约束非逻辑外键确保数据一致性-- 问卷主表 CREATE TABLE questionnaire ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键, title varchar(200) NOT NULL COMMENT 问卷标题, description text COMMENT 问卷描述, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 状态1-启用0-停用, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT问卷主表; -- 题目表关联问卷 CREATE TABLE question ( id bigint(20) NOT NULL AUTO_INCREMENT, q_id bigint(20) NOT NULL COMMENT 所属问卷ID, content varchar(500) NOT NULL COMMENT 题目内容, type tinyint(1) NOT NULL DEFAULT 1 COMMENT 题目类型1-单选2-多选3-填空, sort_order int(11) NOT NULL DEFAULT 0 COMMENT 排序序号, PRIMARY KEY (id), KEY idx_qid (q_id) COMMENT 为关联查询加速, CONSTRAINT fk_question_qid FOREIGN KEY (q_id) REFERENCES questionnaire (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目表; -- 选项表仅用于单选/多选题 CREATE TABLE option ( id bigint(20) NOT NULL AUTO_INCREMENT, q_id bigint(20) NOT NULL COMMENT 所属题目ID, content varchar(200) NOT NULL COMMENT 选项内容, sort_order int(11) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_qid (q_id), CONSTRAINT fk_option_qid FOREIGN KEY (q_id) REFERENCES question (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT选项表;设计理由ON DELETE CASCADE是关键——当删除问卷时其下所有题目及选项自动级联删除避免 MyBatis 手动写多条 delete 语句sort_order字段支持拖拽排序无需修改表结构type字段用 tinyint 而非 enum兼容性更好且便于扩展。3.2 MyBatis 实体类与 Mapper 接口定义遵循“一个表一个实体类一个表一个 Mapper 接口”原则避免过度抽象// Questionnaire.java public class Questionnaire { private Long id; private String title; private String description; private Byte status; // 对应 tinyint(1) private Date createTime; // getter/setter 省略 } // QuestionnaireMapper.java public interface QuestionnaireMapper { // 查询启用中的问卷列表带题目数量统计 ListQuestionnaire selectListWithQuestionCount(Param(status) Byte status); // 根据 ID 查询问卷详情含题目列表 Questionnaire selectByIdWithQuestions(Param(id) Long id); // 插入问卷主记录 int insert(Questionnaire record); // 更新问卷状态 int updateStatus(Param(id) Long id, Param(status) Byte status); }参数说明Param注解在多参数时必不可少否则 MyBatis 无法绑定selectByIdWithQuestions方法名暗示其将关联查询question表实际 SQL 在 XML 中通过resultMap实现嵌套映射。3.3 MyBatis XML 映射文件处理一对多嵌套查询QuestionnaireMapper.xml中定义复杂查询重点看selectByIdWithQuestions的 resultMap!-- resources/mapper/QuestionnaireMapper.xml -- mapper namespacecom.example.survey.mapper.QuestionnaireMapper !-- 定义问卷主表 题目列表的嵌套结果映射 -- resultMap idQuestionnaireWithQuestions typecom.example.survey.model.Questionnaire id propertyid columnid/ result propertytitle columntitle/ result propertydescription columndescription/ result propertystatus columnstatus/ result propertycreateTime columncreate_time/ !-- 关联题目列表collection 标签实现一对多 -- collection propertyquestions ofTypecom.example.survey.model.Question id propertyid columnq_id/ result propertycontent columnq_content/ result propertytype columnq_type/ result propertysortOrder columnq_sort_order/ !-- 题目下的选项列表二级嵌套 -- collection propertyoptions ofTypecom.example.survey.model.Option id propertyid columno_id/ result propertycontent columno_content/ result propertysortOrder columno_sort_order/ /collection /collection /resultMap !-- 查询问卷详情含题目及选项 -- select idselectByIdWithQuestions resultMapQuestionnaireWithQuestions SELECT q.id, q.title, q.description, q.status, q.create_time, -- 题目字段加别名前缀 q_ ques.id AS q_id, ques.content AS q_content, ques.type AS q_type, ques.sort_order AS q_sort_order, -- 选项字段加别名前缀 o_ opt.id AS o_id, opt.content AS o_content, opt.sort_order AS o_sort_order FROM questionnaire q LEFT JOIN question ques ON q.id ques.q_id LEFT JOIN option opt ON ques.id opt.q_id WHERE q.id #{id} ORDER BY ques.sort_order, opt.sort_order /select /mapper避坑提示collection的property必须与实体类中 List 字段名完全一致如questionsSQL 中所有关联表字段必须用AS别名且别名不能与主表字段重名否则 MyBatis 无法正确映射ORDER BY必须包含排序字段否则嵌套集合顺序混乱。4. 问卷 CRUD 与前端交互Layui 表单 SSM 控制器完整链路4.1 新建问卷JSP 表单 SpringMVC Controller 接收create.jsp使用 Layui 表单组件提交至/questionnaire/saveform classlayui-form action${pageContext.request.contextPath}/questionnaire/save methodpost div classlayui-form-item label classlayui-form-label问卷标题/label div classlayui-input-block input typetext nametitle required lay-verifyrequired placeholder请输入标题 classlayui-input /div /div div classlayui-form-item layui-form-text label classlayui-form-label问卷描述/label div classlayui-input-block textarea namedescription placeholder请输入描述 classlayui-textarea/textarea /div /div div classlayui-form-item div classlayui-input-block button classlayui-btn lay-submit lay-filtersaveForm立即提交/button button typereset classlayui-btn layui-btn-primary重置/button /div /div /form script layui.use(form, function(){ var form layui.form; // 监听提交事件 form.on(submit(saveForm), function(data){ // 此处可添加 JS 校验如字数限制 if(data.field.title.length 100){ layer.msg(标题不能超过100字); return false; } }); }); /script关键点methodpost与 ControllerPostMapping匹配name属性值必须与后端Questionnaire实体类字段名一致SpringMVC 才能自动绑定Layui 的lay-verifyrequired提供前端基础校验但后端 Controller 必须二次校验防止绕过。4.2 Controller 层接收表单、调用 Service、返回视图Controller RequestMapping(/questionnaire) public class QuestionnaireController { Autowired private QuestionnaireService questionnaireService; /** * 跳转到新建问卷页面 */ GetMapping(/create) public String createPage() { return questionnaire/create; // 返回 create.jsp 视图名 } /** * 保存问卷含题目与选项 * 注意前端需将题目数组序列化为 JSON 字符串传入 hidden input */ PostMapping(/save) public String save( RequestParam String title, RequestParam(required false) String description, RequestParam String questionsJson, // 前端 JS 序列化的题目JSON RedirectAttributes redirectAttributes) { try { // 1. 构建问卷主对象 Questionnaire questionnaire new Questionnaire(); questionnaire.setTitle(title); questionnaire.setDescription(description); questionnaire.setStatus((byte) 1); questionnaire.setCreateTime(new Date()); // 2. 解析题目JSON示例用 Jackson ObjectMapper mapper new ObjectMapper(); ListQuestionWithOption questionList mapper.readValue( questionsJson, new TypeReferenceListQuestionWithOption() {}); // 3. 调用 Service 保存整套数据含事务 questionnaireService.saveQuestionnaire(questionnaire, questionList); redirectAttributes.addFlashAttribute(msg, 问卷创建成功); return redirect:/questionnaire/list; } catch (Exception e) { redirectAttributes.addFlashAttribute(error, 保存失败 e.getMessage()); return redirect:/questionnaire/create; } } }参数说明RequestParam用于接收表单普通字段questionsJson是前端通过 JS 动态拼装的 JSON 字符串如[{content:喜欢什么颜色,type:1,options:[红,蓝]}]后端解析后交由 Service 层拆解并插入多张表RedirectAttributes用于重定向后传递一次性的提示消息避免 F5 刷新重复提交。4.3 Service 层事务管理保证问卷题目选项原子性Service public class QuestionnaireServiceImpl implements QuestionnaireService { Autowired private QuestionnaireMapper questionnaireMapper; Autowired private QuestionMapper questionMapper; Autowired private OptionMapper optionMapper; /** * 保存问卷及关联题目、选项使用 Transactional 保证事务 */ Override Transactional(rollbackFor Exception.class) public void saveQuestionnaire(Questionnaire questionnaire, ListQuestionWithOption questionList) { // 1. 先保存问卷主表获取生成的主键ID questionnaireMapper.insert(questionnaire); Long qId questionnaire.getId(); // MyBatis 会将自增ID回填到对象 // 2. 遍历题目列表逐个保存题目及选项 for (int i 0; i questionList.size(); i) { QuestionWithOption qwo questionList.get(i); Question question new Question(); question.setQId(qId); question.setContent(qwo.getContent()); question.setType(qwo.getType()); question.setSortOrder(i); // 按前端顺序设置排序 questionMapper.insert(question); Long quesId question.getId(); // 3. 若为选择题保存选项 if (qwo.getType() 1 || qwo.getType() 2) { for (int j 0; j qwo.getOptions().size(); j) { Option option new Option(); option.setQId(quesId); option.setContent(qwo.getOptions().get(j)); option.setSortOrder(j); optionMapper.insert(option); } } } } }事务要点Transactional注解必须作用于 public 方法且由 Spring 容器代理调用才生效rollbackFor Exception.class确保所有异常都触发回滚questionMapper.insert()后立即读取question.getId()依赖 MyBatis 的selectKey或数据库自增特性必须在insert语句中配置useGeneratedKeystrue见 3.3 XML 示例未展开此处默认已配。5. 常见问题排查SSM JSP Layui 组合的 5 个血泪坑5.1 现象Layui 表单提交后页面空白控制台无报错Network 中请求状态为 200 但响应体为空原因Controller 方法返回String视图名如success但springmvc.xml中未配置InternalResourceViewResolver或配置的prefix/suffix与实际 JSP 路径不匹配。例如配置了prefix/WEB-INF/jsp/但 JSP 实际放在webapp/根目录下。解决检查springmvc.xml中视图解析器配置bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value// !-- 注意此处为 /表示 webapp 根目录 -- property namesuffix value.jsp/ /bean同时确认 JSP 文件路径为src/main/webapp/questionnaire/list.jsp而非WEB-INF下。5.2 现象新增问卷时数据库questionnaire表插入成功但question表无数据且无任何异常日志原因QuestionnaireService.saveQuestionnaire()方法上缺少Transactional注解或注解未被 Spring AOP 正确织入常见于 self-invocation即本类中方法 A 调用方法 BB 上有Transactional但此时事务不生效。解决确保Transactional在public方法上且调用方为 Spring 容器管理的 Bean非new QuestionnaireServiceImpl()。若存在 self-invocation将事务方法提取到独立 Service 类中。5.3 现象JSP 页面中${pageContext.request.contextPath}输出为空字符串导致所有静态资源路径 404原因pageContext对象在 JSP 中未正确初始化通常因web.xml中web-app版本声明错误如写成2.3或jsp-api依赖版本与 Tomcat 不兼容。解决确认web.xml头部version4.0与xsi:schemaLocationURL 一致检查 Maven 中jsp-api依赖 scope 为provided且 Tomcat 版本为 8.5兼容 Servlet 4.0。5.4 现象MySQL 中questionnaire.create_time字段存入时间为0000-00-00 00:00:00而非当前时间原因MySQL 5.7 默认开启了STRICT_TRANS_TABLES模式当插入NULL到NOT NULL的datetime字段时不再静默转为0000-00-00而是报错但若代码中未捕获该异常可能被吞掉。更常见的是实体类Date createTime未初始化MyBatis 插入时传入null而数据库字段DEFAULT CURRENT_TIMESTAMP未生效。解决在questionnaire表 DDL 中确保create_time字段定义为datetime NOT NULL DEFAULT CURRENT_TIMESTAMP同时在 Java 实体类中setCreateTime(new Date())必须在insert前执行不依赖数据库默认值。5.5 现象Layui 的layer.msg()提示框不显示控制台报layer is not defined原因layui.js加载顺序错误。常见于将script src.../layui.js/script放在/body之后或在layui.use()调用前未确保layui.js已加载完成。解决严格按 2.3 节示例将layui.js引入放在/body之前并确保layui.use()在script标签内且无其他 JS 阻塞执行。可在浏览器开发者工具 Sources 面板确认layui.js是否加载成功。6. 进阶技巧让问卷系统真正“可用”的 3 个落地细节6.1 问卷填写页动态渲染题目支持实时校验与防重复提交填写页fill.jsp不再是静态 HTML而是由 Controller 根据问卷 ID 查询题目后用 JSTL 循环渲染。关键在于单选/多选题的 name 属性必须唯一且提交时能被后端准确识别。c:forEach items${questionnaire.questions} varq varStatusqStatus div classlayui-form-item label classlayui-form-label${qStatus.index 1}. ${q.content}/label div classlayui-input-block c:choose c:when test${q.type 1} %-- 单选 --% c:forEach items${q.options} varopt varStatusoStatus input typeradio nameanswer_${q.id} value${opt.id} title${opt.content} /c:forEach /c:when c:when test${q.type 2} %-- 多选 --% c:forEach items${q.options} varopt varStatusoStatus input typecheckbox nameanswer_${q.id} value${opt.id} title${opt.content} /c:forEach /c:when c:otherwise %-- 填空 --% input typetext nameanswer_${q.id} placeholder请输入答案 classlayui-input /c:otherwise /c:choose /div /div /c:forEach !-- 防重复提交提交后禁用按钮 -- script layui.use(form, function(){ var form layui.form; form.on(submit(fillForm), function(data){ // 禁用提交按钮防止双击 $(this.elem).addClass(layui-btn-disabled).html(提交中...); // 提交后不刷新页面用 AJAX可选 $.post(${pageContext.request.contextPath}/answer/submit, data.field, function(res){ if(res.code 0){ layer.msg(提交成功, {icon: 1}, function(){ window.location.href ${pageContext.request.contextPath}/; }); } else { layer.msg(提交失败 res.msg, {icon: 2}); $(data.elem).removeClass(layui-btn-disabled).html(重新提交); } }); return false; // 阻止表单默认提交 }); }); /script技巧说明nameanswer_${q.id}确保每个题目答案独立提交后端 Controller 可通过RequestParam MapString, String[] params获取所有答案return false是关键否则表单会双重提交AJAX 提交配合layer.msg()提升体验比整页跳转更轻量。6.2 后台统计页用 Layui 数据表格 ECharts 渲染可视化图表统计页statistic.jsp需两个数据源1题目回答分布用于柱状图2用户填写时间趋势用于折线图。后端提供 JSON 接口RestController RequestMapping(/statistic) public class StatisticController { Autowired private AnswerService answerService; /** * 获取单个题目的选项选择次数统计用于 ECharts 柱状图 */ GetMapping(/optionCount) public ResultMapString, Object getOptionCount(RequestParam Long questionId) { MapString, Object data answerService.countOptionByQuestionId(questionId); return Result.success(data); } /** * 获取近7天每日填写人数用于 ECharts 折线图 */ GetMapping(/dailyCount) public ResultListMapString, Object getDailyCount(RequestParam Long questionnaireId) { ListMapString, Object data answerService.countDailyByQId(questionnaireId); return Result.success(data); } }前端 ECharts 初始化精简版div idchart1 stylewidth: 100%; height: 400px;/div div idchart2 stylewidth: 100%; height: 400px;/div script src${pageContext.request.contextPath}/static/echarts/echarts.min.js/script script // 初始化柱状图 var chart1 echarts.init(document.getElementById(chart1)); $.get(${pageContext.request.contextPath}/statistic/optionCount?questionId${qId}, function(res){ if(res.code 0){ var data res.data; chart1.setOption({ title: { text: 选项选择次数统计 }, tooltip: {}, xAxis: { data: data.labels }, // [红,蓝,绿] yAxis: {}, series: [{ name: 次数, type: bar, data: data.values // [12, 8, 5] }] }); } }); // 初始化折线图类似逻辑 /script落地价值ECharts 图表让数据一目了然比纯表格更易向非技术人员汇报Result封装统一返回格式code,msg,data前端可复用处理逻辑$.get异步加载不影响页面初始渲染速度。6.3 权限与安全加固基于 Session 的简易登录拦截问卷系统虽不涉敏感数据但需防止未授权访问后台。在web.xml中添加 Filter!-- web.xml -- filter filter-nameloginFilter/filter-name filter-classcom.example.survey.filter.LoginFilter/filter-class /filter filter-mapping filter-nameloginFilter/filter-name url-pattern/admin/*/url-pattern !-- 仅拦截 admin 目录 -- /filter-mappingLoginFilter.java实现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; // 白名单登录页、登录接口放行 String uri request.getRequestURI(); if (uri.contains(/admin/login) || uri.contains(/admin/doLogin)) { chain.doFilter(req, resp); return; } // 检查 Session 中是否存在用户标识 HttpSession session request.getSession(false); if (session null || session.getAttribute(adminUser) null) { response.sendRedirect(request.getContextPath() /admin/login); return; } chain.doFilter(req, resp); } }安全边界此方案不替代专业权限框架如 Shiro本文还有配套的精品资源点击获取