
简介jsp期末大作业社团管理系统源代码.zip 提供了一套基于 Servlet JSP 的 Java Web 社团管理项目面向正在完成期末大作业或初学 Java Web 的开发者。系统围绕社团成员与管理员两类角色实现了请假申请与审批、退团请求处理、匿名信息管理、学生信息增删查改、空课表查询等常见功能并多处使用 Ajax 提升交互体验。压缩包共含164个文件整体大小2.51MB以 Java 源文件与 JSP 页面为主同时包含编译后的 class 文件、CSS 样式、JavaScript 脚本、jar 库以及项目与数据库配置信息并附有图片和说明文档结构清晰便于导入开发环境进行查看与调试。目前已有1451人学习浏览。通过这份源码可以快速理解后台 Servlet 的请求分发与处理方法、管理员模块的包级目录划分方式以及 JSPServlet 整合开发的整体流程。无论是用于期末答辩的代码讲解、课程设计参考还是基于现有功能二次扩展这套项目都能提供相对完整的起点。1. 拿到这份 jsp 期末大作业社团管理系统源代码.zip先别急着解压临近期末课程群里最常出现的求助不是“这道题怎么做”而是“有没有能直接跑的 JSP 项目”。这个 jsp 期末大作业社团管理系统源代码.zip拆开看就是一套标准的 JavaWeb 三件套JSP 做页面展示、Servlet 做请求控制、MySQL 存业务数据加上社团、成员、活动、入团申请这几张主表。它解决的是一个很具体的需求你下周要交课设需要一份能站起来讲、能演示、能改代码的项目底稿而不是一个需要自己从头搭的框架。它的受众很明确计算机专业大三左右的学生或者想快速复习 JavaWeb 分层结构、Tomcat 部署和 JDBC 的从业者。别期待它带前后端分离、Spring Boot 和 Redis它不是。但它能把 JavaWeb 必考的注册登录、CRUD、Session、数据库连接这几个知识点原样摆齐晚上十点开始弄凌晨能跑起来这是它最值钱的地方。2. 为什么期末 JSP 项目长这样技术栈选型、三层结构与核心表设计2.1 为什么交作业选 JSP Servlet MySQL而不是 Spring Boot这是很多人拿到源码后第一个疑问现在公司里都写 Spring Boot为什么课程设计还卡在 JSP答案不在于“先进”而在于“考察点”。JSP 是 Servlet 的一种模板实现JSP 文件第一次被访问时会由 Tomcat 翻译成对应的 Java 类再编译执行这个过程中你能看到 HTTP 请求如何进入服务器、Session 如何维持、页面如何渲染。Spring Boot 把这些全部封装成注解和自动配置答辩时老师问“请求从浏览器到数据库中间发生了什么”你只能回答“不知道框架处理了”。而用 JSP 写的项目即使是最朴素的写法也能讲清楚 JSP→Servlet→DAO→MySQL 这条完整链路。常见的课程设计要求不是用最先进的技术而是用最基础的技术把业务做完整。这份社团管理系统需要覆盖用户注册登录、社团创建、入团申请审批、活动发布、活动报名这几个动作刚好落在 JSP Servlet MySQL 的能力范围内不需要引入 Redis 做缓存也不需要 MQ 做异步。所以拿到源码后第一件事不是改代码而是确认技术栈版本能对上能对上就能跑不能对上就会出现第 5 章那类兼容性翻车。2.2 源码包里的常见目录页面层、控制层与 DAO 的边界解压 zip 后典型的工程结构是这样的源码目录下有一个放 Java 类的src下面按com.xxx.servlet、com.xxx.dao、com.xxx.entity分包webapp或WebContent目录下放着WEB-INF/web.xml和一堆.jsp文件还有放 CSS、JS、图片的static或css目录。这份代码不会有多复杂的包结构因为课程设计通常只需要你体现分层意识不需要体现微服务意识。控制层由 Servlet 承担每个功能点对应一个 Servlet 类比如LoginServlet、RegisterServlet、ClubApplyServlet。它们负责接收request参数、调用 DAO、把结果放进request或session、最后forward或redirect到 JSP。JSP 页面只负责展示不要在里面写Connection conn DriverManager.getConnection(...)这种连接代码。我见过不少课设把数据库逻辑写在 JSP 的%! %声明里能跑但答辩时老师一眼就能看出没有分层意识分数会往下压。DAO 层则负责与数据库交互常见的形式是一个DBUtil工具类提供连接各个 DAO 类写findByUsername、insertClub、updateStatus这类方法。entity 层对应表结构字段和表字段一一对应。2.3 核心表设计五张业务表加一个管理员表把业务拆成什么样子社团管理系统的业务边界很清晰核心诉求是“有人创建社团有人申请加入社团发布活动成员参加活动”。按这个诉求拆解最少需要五张业务表t_student用户表存学号、姓名、密码、院系和联系方式t_club社团表存社团名称、简介、创建人和创建时间t_activity活动表存活动名称、地点、开始时间和结束时间t_apply入团申请表存申请人和目标社团以及审批状态t_member成员表存学生和社团的从属关系。另外通常还有一张t_admin管理员表单独存放后台登录账号不和学生表混在一起因为管理员没有院系学号这些学生属性。表之间的关联是设计的核心。t_club.founder_id指向学生表表示谁是创建人t_apply同时指向学生表和社团表表示一次申请涉及的两个主体t_member也是同时指向学生表和社团表但与t_apply不同的是它记录的是已经生效的从属关系。活动表如果有报名功能还要加一张中间表来记录“谁报名了哪个活动”否则一个活动对应多个学生只能用逗号拼接字段查询和统计都会很难受。如果你拿到的源码里活动报名是逗号拼接不要急着改先理解这种写法的缺陷答辩时能说出“这里可以用中间表优化”本身就是加分项。3. 本地跑通社团管理系统的最小路径环境、导入与数据库初始化3.1 版本匹配矩阵JDK 8Tomcat 8.5MySQL 5.7 最稳妥拿到 zip 后第一件事不是双击运行而是核对环境版本。JSP 项目的年代感通常很强很多源码是 2018 年前后写的那个年代的标配是 JDK 1.8、Tomcat 8.5、MySQL 5.7。如果你手边装的是 JDK 17 和 Tomcat 10大概率会踩两个坑一是老代码里的javax.servlet依赖在 Tomcat 10 变成了jakarta.servlet类名变了直接编译失败二是 MySQL 8.0 的加密方式与老驱动不兼容连接会被服务端拒绝。我一般建议这个组合组件推荐版本原因JDK1.8老项目基本按 JDK 8 语法写的高版本 JDK 也能跑但没必要冒险Tomcat8.5 或 9.0Tomcat 9 仍使用javax.servlet与老源码兼容MySQL5.7连接驱动用com.mysql.jdbc.Driver可直接工作MySQL 驱动 jarmysql-connector-java 5.1.49支持 5.7 的加密协议且兼容 JDK 8如果你本机已经是 MySQL 8后面第 5 章会说怎么改驱动和连接串。这里先说结论拿到该项目的第一决策是把环境往老版本靠拢而不是把代码往新环境改因为源码只有一个环境可以重装。做完这一步后面导入项目的 80% 的坑已经没了。3.2 从 zip 解压到 IDEA 工程导入步骤与两个必改配置假设你已经装好 IDEA 和 Tomcat接下来是把解压出来的文件夹变成能跑的项目。常见做法是在 IDEA 里选择Open直接打开解压后的目录IDEA 会识别出这是一个 Maven 项目或普通 Web 项目。如果源码里带了pom.xmlIDEA 会自动下载依赖如果没带说明它是直接把 jar 包放在WEB-INF/lib下的传统结构这种情况不需要 Maven但要把lib目录下的 jar 添加为 Library。第一步打开Project Structure快捷键CtrlAltShiftS在Libraries里确认mysql-connector-java已经被引用。第二步在Facets里把Web特性关联到项目并指定web.xml路径。第三步在Artifacts里添加一个Web Application: Exploded类型的构建产物。这三步做完项目才能被 Tomcat 识别为可部署的 Web 应用。然后在Run Configuration里新建一个Tomcat Server - Local把Application server指向你的 Tomcat 目录在Deployment标签里把这个构建产物加进去。这里有一个必改配置Application context建议改成/club或空路径/。如果不改默认访问路径会带一串项目名比如http://localhost:8080/jsp_末大作业_社团管理系统_war_exploded/这串字符又长又容易错改成/club后访问地址是http://localhost:8080/club/index.jsp清晰得多。3.3 数据库初始化执行项目里的 init.sql 而不是用工具同步接下来是初始化数据库。项目包里通常带一个sql文件夹里面有一个club.sql或init.sql。不要打开 Navicat 直接建表而是先在 MySQL 里建好库再通过命令行或 Navicat 执行这个 SQL 文件。先执行库的创建然后切进库再执行建表否则脚本里的USE语句可能导致表建到别的库里。建表脚本一般长这样CREATE DATABASE IF NOT EXISTS club_db DEFAULT CHARACTER SET utf8mb4; USE club_db; CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, password VARCHAR(64) NOT NULL COMMENT 密码, department VARCHAR(50) DEFAULT NULL COMMENT 院系, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_club ( id INT PRIMARY KEY AUTO_INCREMENT, club_name VARCHAR(100) NOT NULL, intro TEXT, founder_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_club_founder FOREIGN KEY (founder_id) REFERENCES t_student(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段脚本的逻辑是先创建数据库club_db然后建t_student和t_club两张表。字符集选utf8mb4而不是utf8因为多数课程设计源码是早期写的utf8在 MySQL 里最多存三个字节的字符遇到中文没问题遇到某些特殊符号会报“Incorrect string value”。后面几张活动表、申请表、成员表结构与这两张类似关键是注意外键约束t_club.founder_id引用t_student.id这个约束保证插入社团时创始人一定存在于学生表中。实际执行时如果发现表结构有字段对不上 Java 代码里的实体类优先以 SQL 脚本为准因为你改 Java 代码比改 SQL 复杂得多而且数据库一旦有数据改表结构后重新导入的开销更大。3.4 数据库连接串DBUtil 里最常出错的三个参数跑通之前一定要检查DBUtil.java里的连接串。多数课程设计用 JDBC 直连这是最简单的方案也最容易因为环境差异报错。连接代码类似public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/club_db?useSSLfalsecharacterEncodingUTF-8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; public static Connection getConnection() { Connection conn null; try { Class.forName(DRIVER); conn DriverManager.getConnection(URL, USER, PASSWORD); } catch (ClassNotFoundException e) { e.printStackTrace(); System.out.println(数据库驱动加载失败请检查驱动 jar 是否在 WEB-INF/lib 下); } catch (SQLException e) { e.printStackTrace(); System.out.println(数据库连接失败请检查 URL、用户名、密码是否与本地 MySQL 一致); } return conn; } }这段代码有三个地方是高频故障点。第一DRIVER这个字符串如果你用的是 MySQL 8.0 的驱动包com.mysql.jdbc.Driver已经废弃要改成com.mysql.cj.jdbc.Driver。第二PASSWORD必须是本地 MySQL root 用户的真实密码很多课设源码默认写123456或root你本机密码不同就要改这是数据库连不上的头号原因。第三URL 里的useSSLfalse和serverTimezoneAsia/Shanghai参数在高版本 JDBC 驱动下是必需的前者避免安全连接握手后者解决时区导致的The server time zone value报错。改完这些后建议写一个简单的main方法直接调用getConnection()验证连接是否成功不要等 Tomcat 起来后再去页面上看错误那样排查链路太长。4. 按功能模块读源码登录、审批、活动报名与页面图片定位4.1 登录与注册Servlet 生命周期、Session 与数据校验登录功能是 JavaWeb 课设里必被问到的点它串起了整个 Servlet 生命周期容器启动后LoginServlet被实例化请求到来时service()分配线程调用doGet或doPost方法里通过request.getParameter(username)拿前端表单数据调用 DAO 的查询方法匹配成功则把用户对象放进session然后重定向到首页。核心代码如下WebServlet(/login) public class LoginServlet extends HttpServlet { private StudentDao studentDao new StudentDao(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); if (username null || password null || username.trim().isEmpty()) { request.setAttribute(error, 用户名和密码不能为空); request.getRequestDispatcher(login.jsp).forward(request, response); return; } Student student studentDao.findByUsernameAndPassword(username, password); if (student ! null) { HttpSession session request.getSession(); session.setAttribute(loginUser, student); response.sendRedirect(request.getContextPath() /index.jsp); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } }逻辑很直白先判空再做数据库查询成功后写session并重定向失败则返回登录页并带错误消息。注意这里判断成功的依据是findByUsernameAndPassword返回的对象是否为空隐患是 SQL 拼接容易被注入第 6 章会提到怎么改成PreparedStatement的预处理写法。另外这里的WebServlet(/login)注解是 Servlet 3.0 的特性如果项目里的web.xml版本低于 3.0注解是不生效的你需要在web.xml里手写servlet和servlet-mapping否则访问/login会 404。这是老课设项目很常见的差异。4.2 社团申请与审批状态字段驱动业务流转入团申请是社团管理系统的核心流程代码里通常用一张申请表加一个状态字段实现。t_apply表里的status字段只有三个值0表示待审批1表示通过2表示拒绝。这个字段的意义在于列表页可以按状态过滤学生端看到的是“我申请了哪些社团、目前是什么状态”管理端看到的是“现在有哪些待审批的申请”。审批的 SQL 更新通常长这样UPDATE t_apply SET status 1, audit_time NOW() WHERE id ? AND status 0;注意这里的AND status 0不是多余的。它保证了一个并发场景下的正确性如果管理员在 A 页面审批同一条申请而另一台设备的 B 管理员也打开这条申请两人同时点击通过理论上只应该有一条生效。这个WHERE status 0就是一个乐观锁的简易实现让第二次更新影响行数为 0通过返回值就能知道已经被审批过了。代码层面只需调用int rows applyDao.updateStatus(id, 1); if (rows 0) { // 提示已被处理 }即可。很多课设项目不写这个条件直接UPDATE t_apply SET status 1 WHERE id ?功能上没区别但答辩时如果你能讲出“用状态条件防止并发重复审批”会非常加分。4.3 活动发布与报名中间表落法与事务问题活动发布功能比前两个复杂一点因为它涉及两张表的写入活动本身以及创建者默认成为活动参与者。如果不小心会做成“活动插入成功但参加记录没有插入”的脏数据。正确做法是把两个 INSERT 放到同一个事务里代码如下public boolean publishActivity(Activity activity, int creatorId) throws Exception { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); ActivityDao activityDao new ActivityDao(); int activityId activityDao.insert(conn, activity); ActivitySignDao signDao new ActivitySignDao(); signDao.insert(conn, activityId, creatorId); conn.commit(); return true; } catch (Exception e) { if (conn ! null) conn.rollback(); throw e; } finally { if (conn ! null) conn.close(); } }这段代码的关键点是conn.setAutoCommit(false)和conn.commit()它们把两次 INSERT 变成一个原子操作。如果中间任何一步抛出异常rollback()会把已经插入的活动数据撤销。很多课设代码不处理事务活动发布成功后报名表里查不到创建人这看起来是小问题但演示的时候老师可能会问“你自己创建的活动报名列表里为什么没有你”这时你再改就晚了。还有一个细节这个代码里的insert方法接收conn参数这是为了让两个 DAO 方法共享同一个连接如果每个 DAO 方法内部自己开连接事务就失效了因为那是两个不同的数据库会话。4.4 JSP 页面图片坐标定位CSS 绝对定位在活动海报上的用法“jsp 图片如何对坐标定位”是很多人在改页面时搜索的高频词。这里的本质不是 JSP 语法而是 HTML 和 CSS 的定位问题。JSP 页面最终输出的是 HTML所以图片坐标定位的答案在 CSS 里。常见场景是社团首页放一张活动宣传图想在图的某个坐标位置叠一个社团 Logo 或“报名入口”按钮。推荐的写法是父容器相对定位图片作为底层需要叠加的元素用绝对定位div styleposition: relative; width: 400px; height: 250px; img srcactivity_banner.jpg stylewidth: 100%; height: 100%; alt活动横幅 span styleposition: absolute; left: 30px; bottom: 20px; color: #fff; font-size: 18px; background: rgba(0,0,0,0.6); padding: 6px 12px; 社团招新报名 /span /div这里position: relative声明了父容器是定位参考系position: absolute; left: 30px; bottom: 20px让文本相对父容器左下角偏移。坐标定位的精髓在于坐标系absolute 的参考点不是浏览器窗口而是最近的开启了position的祖先元素。如果你忘了给父容器加relative绝对定位会一路找到页面根节点坐标值就完全对不上了。在调整坐标时我通常先开浏览器开发者工具F12在 Elements 面板里直接改 left/bottom 数值拖动出合适位置后再把最终数值填回 JSP 文件这样比盲改代码快很多。5. 期末作业最爱翻车的 5 个环境坑现象、原因与修复顺序5.1 现象一项目导入后 javax.servlet 大面积标红现象IDEA 里一打开项目所有import javax.servlet.http.HttpServlet;和doGet/doPost方法都标红编译时也报“找不到包”。原因IDEA 项目没有关联 Tomcat 的库。javax.servlet不是 JDK 自带的它在 Tomcat 安装目录的lib/servlet-api.jar里。解压的源码没有这个 jar因为 Tomcat 容器在运行时会提供它但在编译阶段需要 IDEA 帮你把依赖引进来。解决打开Project Structure - Modules - Dependencies点击号选择Library - Tomcat把本地 Tomcat 添加进去。如果你的WEB-INF/lib下已经有servlet-api.jar也可以直接把那个 jar 加为 Library。添加后标红基本消失如果还红检查 IDEA 的 SDK 是否选到了正确的 JDK。5.2 现象二运行后立即报数据库连接错误现象启动 Tomcat 不报错但一访问登录页面就出现Communications link failure或Access denied for user rootlocalhost后台日志一大片红色异常。原因两种情况对应两个原因。Communications link failure多半是 MySQL 服务没启动或 MySQL 是 8.0 而驱动还是老版本Access denied则是 DBUtil 里的用户名或密码与本地 MySQL 不一致或者 root 账号不允许远程连接。解决先确认 MySQL 服务已启动命令行执行mysql -u root -p能进得去。然后核对 DBUtil 里的用户名密码这是一个最容易被忽略的点很多源码默认密码是123456你本机 root 实际密码不是它。如果是 MySQL 8.0 版本按第 3 章把驱动改为com.mysql.cj.jdbc.DriverURL 里加useSSLfalseserverTimezoneAsia/Shanghai。5.3 现象三页面中文全是乱码现象浏览器里看到的是锟斤拷或??数据库里表查出来也是问号。原因三层字符集不一致。第一层是 JSP 文件本身的编码可能是存成了ISO-8859-1第二层是页面声明的contentType与文件编码不符第三层是 JDBC 连接串没有指定characterEncodingUTF-8导致数据库读写时用了默认字符集。解决检查 JSP 文件头是否有% page contentTypetext/html;charsetUTF-8 languagejava %没有就加上。检查文件本身编码是否为 UTF-8IDEA 右下角能看到当前编码如果不是就转换。最后在 DBUtil 的 URL 上追加上文提到的characterEncodingUTF-8。三层都改对后重启 Tomcat 而不是热部署刷新页面。5.4 现象四改了 JSP 页面却不生效还是旧页面现象修改index.jsp里的标题或文字后刷新浏览器看到的还是修改前的内容有时 IDEA 会提示“静态资源已修改但未部署”。原因这大概率不是缓存问题而是 Tomcat 运行时的输出目录与源码目录是两份。IDEA 里 Tomcat 默认部署的是 Artifact 的输出目录不是源码目录。你改了源码里的index.jsp但输出目录里那份没更新。解决在Run Configuration - Deployment里把Exploded的构建方式改为Update resources并在On frame deactivation选项选择Update resources。这样每次切回 IDEA 窗口改动会自动同步到输出目录。如果急着看效果重启 Tomcat 是最可靠的姿势。很多新手在这个问题上卡半个多小时归根结底是不理解“部署”是把项目复制了一份到 Tomcat 的 webapps 或 IDEA 的 out 目录而不是直接运行源码目录。5.5 现象五8080 端口被占用Tomcat 启动失败现象点击启动 Tomcat日志立刻报Port already in use: 8080然后启动失败浏览器访问localhost:8080是其他程序或另一个旧 Tomcat。原因端口被系统里残留的 Java 进程或另一个 Tomcat 实例占用。如果你之前多次非正常关闭 Tomcat会有后台 java.exe 进程没退干净。解决Windows 下打开命令行执行netstat -ano | findstr 8080最后一列是占用端口的进程 PID根据 PID 在任务管理器里结束对应进程。不想杀进程的话最省事的方法是把 Tomcat 端口改掉打开 Tomcat 安装目录下conf/server.xml把Connector port8080 ...改成8081然后重启。我习惯把端口改成 8081因为 8080 太容易被占用课设演示现场突然端口冲突是最尴尬的翻车。6. 交作业前的最后升级密码加盐、演示脚本与答辩节奏6.1 把明文密码改成加盐哈希答辩时最能讲的三句话课程设计的登录表里密码字段多半是明文存储甚至还有用 MD5 直接存摘要的。如果演示时老师要求查看数据库看到password字段存的是一串看得懂的密码会显得很不专业。这里有一个成本极低、效果极好的升级改成“加盐 SHA-256”处理。不要用 MD5MD5 已经能被彩虹表批量碰撞。实现也不复杂注册时生成随机盐值把盐拼到密码后面再做 SHA-256 摘要数据库同时存摘要和盐值。登录时取出该用户的盐重新计算摘要比对。你可以准备这样一小段代码public static String getSaltedPassword(String rawPassword, String salt) { String input salt rawPassword; try { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] digest md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } }这段代码没有引入任何第三方依赖直接使用 JDK 自带的MessageDigest。salt rawPassword的拼接顺序无所谓但要保证注册和登录时用的是同一个规则。答辩时你能讲清楚外行听的是逻辑内行看的是你至少知道“密码不能明文存、不能直接用 MD5”这个安全基线。这是整个课设里性价比最高的改进之一。6.2 一份 15 分钟演示脚本先演示业务再讲技术栈最后说一点演示和组织层面的经验。期末答辩通常只有 10 到 15 分钟很多人一上来就讲“我用了 JSP、Servlet、MySQL分层清晰”讲完已经过去 5 分钟然后进入演示环节手忙脚乱。我这里强调一个原则先演示后讲架构。打开浏览器先走一遍游客视角的注册流程、登录、申请加入社团、管理员审批、活动报名看完一遍之后老师对系统已经有了直观印象这时你再讲技术栈、讲表结构、讲代码里的关键设计他被说服的成本会低很多。演示脚本大致可以分四段前 2 分钟展示登录注册中间 5 分钟演示一个完整业务闭环创建一个社团、申请加入、进入后台审批、发布活动、报名活动再用 3 分钟展示数据库在 MySQL 里查t_apply的status字段变化搜一段你写的 DAO 方法最后 5 分钟留给提问。演示时有一个小技巧登录后把页面刷新一遍让老师看到 Session 依然保持在线这直接证明session是有效的而不是页面写死的假登录状态。我的习惯是每次答辩前一天都把项目从零部署一遍把 MySQL 服务停掉再启动一次 Tomcat清空浏览器缓存走完全部流程。这个动作帮我拦下过至少三次“数据库没启动”和“代码改了没部署”的现场翻车。交作业这件事真正拉开差距的不是功能写得多全而是你对自己代码里每个细节都有解释能力并能证明它在干净环境里能跑通。做到这一步这门课的验收已经稳了希望这些血泪经验能帮到你少熬夜。本文还有配套的精品资源点击获取