简介:面向正在学习JavaWeb开发的初、中级读者,这套基于JavaEE的网上评教系统项目以JSP+Servlet+MySQL构建完整业务闭环,覆盖从用户登录、课程选择到评教打分的主要环节,适用于课程设计、毕业设计或项目实训。资源包共651个文件,压缩包约11.71MB,内部以JSP动态页面、CSS样式表、GIF界面演示图、JavaScript交互脚本和SQL数据库脚本为主,还附有classpath、project等工程配置,便于导入IDE后直接运行和二次开发。目前已有110人学习浏览。从内容预览可见,项目包含多处功能页面的样式体系与页面结构,结合源码和数据库脚本,可完整还原网上评教系统的开发思路:包括用户角色权限、课程与教师信息管理、评教评分逻辑、JDBC数据持久化,以及MVC分层架构组织。对于希望掌握JSP网站设计或快速搭建同类教务系统的读者,这是一份结构清晰、可直接落地运行的完整学习素材,能帮助理解企业级JavaWeb应用从数据表设计到前端展示的典型实现。
1. 网上评教系统:为什么一个十几张 JSP 页面的课设,让那么多人翻车
拿到“基于JavaEE的网上评教系统_JSP网站设计_MySQL数据库设计.rar”这个名字,基本能猜出它是什么来路:高校里最常见的 JavaWeb 课程设计题目,压缩包里装着一个用 JSP + Servlet + MySQL 搭的师生互评网站。网上评教系统的核心业务很直接——学生登录后给任课教师打分、写评语,教师登录后查看评分结果,管理员负责维护课程信息和统计报表。功能一眼能看完,但真要做出来并跑起来,涉及的角色权限、数据表关联、Servlet 跳转和部署排错,每一步都有坑。这篇文章就按一个可交付的课设标准,把技术选型、数据库设计、Servlet 实现和部署排查整条链路讲清楚,适合正在做同类题目、或者想快速上手 JavaWeb 完整项目的读者跟着复现。
2. 技术栈选型:JavaEE 组合拳为什么还没被 Spring Boot 完全取代
网上评教系统这类项目,最常见的检索叫法是“javaweb 项目完整案例 mysql”,说明它的定位就是教科书式的 Java Web 全链路演示。JSP 负责页面展示、Servlet 负责请求控制、MySQL 负责持久化存储,这套组合看起来笨重,却几乎是课程设计和毕业答辩的默认答案。
2.1 JavaEE 在这里到底指什么
严格说,这个标题里的 JavaEE 并不是完整的 EJB、JMS 那套企业级规范,而是 Java Web 开发中用到的那一截:Servlet、JSP、JDBC 以及 Tomcat 容器。教育资源里说的“JavaEE”,多数时候就是指这种三层架构下的 Web 应用开发。很多学生把 JavaEE 理解成必须用 SSH 或 SSM 框架,其实对于评教系统这种 CRUD 为主的项目,Servlet + JSP 就已经能覆盖全部需求。
选择这套技术栈的理由很实际:
- Servlet 能直观展示请求生命周期,答辩时讲得清楚,不会被追问框架底层原理。
- JSP 本身就是模板引擎,服务端渲染页面,
.jsp文件直接放 WebRoot 下就能被 Tomcat 编译执行,没有前端打包的概念。 - MySQL 免费、安装资料多,出问题时热搜词里随便一搜就是完整教程,包括“mysql 8.0 安装配置”“mysql 5.7.44 安装过程详细”这类保姆级内容。
相比 Spring Boot,用 JSP 还有一个实际优势:部署物就是一个 WAR 包或整个 Web 目录,扔到 Tomcat 的 webapps 里就能跑,不涉及内嵌服务器和外置服务器混用的困惑。如果换成 Spring Boot + JSP 的组合,反而要额外处理 JSP 在 jar 包和 war 包下的编译差异,对新手来说更容易翻车。
2.2 JSP 和 Servlet 在这个系统里的分工边界
评教系统最常见的错误是 JSP 里写大量 Java 代码,页面和逻辑完全耦合。正确的分工是:JSP 只做渲染,Servlet 只做控制,业务与数据库操作放进独立的 Java 类里。三层结构在代码层面就是三个包,下面是一个标准的包结构:
src/ ├── com/edu/rating/ │ ├── controller/ # Servlet,只干三件事:收参、调服务、跳转 │ │ ├── LoginServlet.java │ │ ├── StudentRatingServlet.java │ │ └── TeacherQueryServlet.java │ ├── service/ # 业务层,处理评分、统计、权限校验 │ │ ├── RatingService.java │ │ └── CourseService.java │ ├── dao/ # 数据访问层,封装 JDBC 操作 │ │ ├── UserDao.java │ │ └── RatingDao.java │ └── util/ │ ├── DBUtil.java # 数据库连接工具 │ └── StringUtil.java └── webapp/ # JSP 页面与静态资源 ├── WEB-INF/ │ ├── web.xml │ └── lib/mysql-connector-java.jar ├── login.jsp ├── student/ │ ├── rating_list.jsp │ └── rating_submit.jsp └── teacher/ └── rating_result.jsp这个包结构里每个类都只做一件事:controller 里不写 SQL,dao 里不写 HTML,jsp 页面里不写业务判断。比如登录,页面提交表单后由LoginServlet接收参数,调用UserService按用户名和角色身份查询,再把结果用request.getRequestDispatcher(...).forward(...)跳转。工程化程度不高,但足够让答辩老师一眼看出项目结构是清楚的。
2.3 为什么这个项目值得用手工 JDBC 而不是 ORM
JavaWeb 课设阶段,很多人纠结要不要上 MyBatis。评教系统一共五六张表,每张表就两三个主要查询,MyBatis 的配置成本可能比写 JDBC 还高。JDBC 五个步骤——加载驱动、获取连接、预编译语句、执行查询、释放资源——写一次就熟悉一次,面试被问到“JDBC 和 ORM 框架的区别”时也有真实体验垫底。这里的取舍是:用 MySQL 的常用命令(比如show tables;、desc rating;)查看表结构,配合少量手写 JDBC 增删改查,能让整个项目的数据流转全程透明。
如果最终论文里想强调工程性,可以加一层数据库连接池。阿里巴巴的 Druid 包很小,替换DriverManager.getConnection()的逻辑只需要十几行,效果却是“启动慢但运行稳”。这种小优化写进课设文档里,比单纯多写两个页面要有说服力。
3. 搭建开发环境:JDK、Tomcat、MySQL 8.0 的版本配对与踩坑
网上评教系统对环境的要求不高,但版本配对一旦出错,最典型的症状就是 Tomcat 起不来或 JSP 页面报错。常见做法是 JDK 1.8 + Tomcat 8.5/9 + MySQL 8.0,这个组合资料最多,问题也最好查。
3.1 JDK 与 Tomcat 的版本匹配,老生常谈但不得不谈
JDK 版本和 Tomcat 版本需要严格对应。Tomcat 8.5 支持 JDK 8 到 JDK 11,Tomcat 9 支持 JDK 8 到 JDK 17,如果用 JDK 17 配 Tomcat 8.5,启动时有可能出现UnsupportedClassVersionError或 HTTPSession 相关的兼容异常。稳妥的选择是 JDK 8 + Tomcat 8.5,因为大部分课设参考代码都是用 JDK 8 编译的,避免把时间耗在无意义的版本升级上。
装 JDK 时注意一个高频翻车点:安装路径不能有空格和中文。C:\Program Files\Java虽然是默认路径,但某些老版本 JSP 编译脚本对带空格的路径处理不友好,环境变量也容易配错。我一般会改成D:\Java\jdk1.8.0_291这样的短路径,然后把JAVA_HOME和Path配好,命令行验证:
java -version javac -version echo %JAVA_HOME% startup.batstartup.bat是 Tomcat 的启动脚本,双击后如果闪退,多半是JAVA_HOME没设对。验证 Tomcat 是否起来了,用curl http://localhost:8080看返回状态,或者直接打开浏览器访问。
顺带提一下编辑器选择。如果不想装体积庞大的 IDEA,用 VSCode 写 JSP 工程也完全可行,安装 Java Extension Pack 就能获得语法提示,再装一个 Tomcat for Java 插件直接启动调试。热词里“vscode 配置 javaee 语言环境”指的就是这套插件组合,对课设来说绰绰有余。
3.2 MySQL 安装时的三个关键参数
MySQL 安装是这套系统里最容易提前劝退人的环节。热词里“mysql 安装教程 8.0”“mysql 安装配置教程”刷屏不是没理由的,安装过程中几个默认选项如果没改,后面连接数据库时全是坑。重点看三个地方:
- 字符集必须选
utf8mb4,默认的utf8在 MySQL 8.0 里存不了完整的中文和 emoji,JSP 页面显示评语时会出现?或乱码。 - 认证方式选
Use Strong Password Encryption,这是 8.0 默认的caching_sha2_password,如果你的 JDBC 驱动版本太旧,连接时会报Public Key Retrieval is not allowed,后面在 URL 里加allowPublicKeyRetrieval=true解决。 - 端口保持 3306,但要确认本机没有其他 MySQL 实例占用。曾经遇到一次
net start mysql后服务一直显示“服务无法启动”,最后排查发现是另一个残留 MySQL 进程占用了 3306。
安装完成后,用命令行验证能不能正常登录,顺手创建课设专用的数据库和用户:
mysql -u root -p CREATE DATABASE rating_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'rating_user'@'localhost' IDENTIFIED BY 'Rating@2024'; GRANT ALL PRIVILEGES ON rating_system.* TO 'rating_user'@'localhost'; FLUSH PRIVILEGES;这里单独建一个用户而不是直接用 root,是为了让项目的 DBUtil 配置贴近真实生产环境。后面部署到别的机器时,只需要改一个配置文件,不用动系统级账号。如果在本机用 Docker 装 MySQL,要点是端口映射要写成-p 3306:3306,否则宿主机连接时会报Can't connect to MySQL server。Docker 安装 MySQL 失败很多时候不是镜像问题,而是忘记挂载配置导致字符集不对。
4. 数据库设计:网上评教系统的核心不是页面,而是表和索引
如果一个评教系统翻车,十有八九是评教记录表设计出了问题。页面写错了可以改,表结构错了意味着所有关联查询都得重写。在设计阶段多花半小时,后面少熬夜。
4.1 五张核心表,对应完整评教流程
学生评教一次需要知道“评的是哪门课、哪个老师、打几分、什么时候评的”,所以最基础的表结构是五张:学生表、教师表、课程表、评教指标表、评教记录表。用户名和密码可以统一放在用户表里,用角色字段区分学生和教师,也可以直接拆开,看你论文里怎么定义。下面用user表承载登录信息,student、teacher两张表存各自扩展信息:
CREATE TABLE sys_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(20) NOT NULL COMMENT 'STUDENT/TEACHER/ADMIN', real_name VARCHAR(50) COMMENT 'JSP 个人信息展示页面里的姓名', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE course ( course_id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL, teacher_id INT NOT NULL, semester VARCHAR(20) NOT NULL COMMENT '如 2024-2025-1', KEY idx_teacher_id (teacher_id), CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES sys_user(user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE rating_record ( rating_id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, teacher_id INT NOT NULL, score TINYINT NOT NULL COMMENT '1-10 分', comment TEXT, rated_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course (student_id, course_id), CONSTRAINT fk_rating_student FOREIGN KEY (student_id) REFERENCES sys_user(user_id), CONSTRAINT fk_rating_course FOREIGN KEY (course_id) REFERENCES course(course_id), CONSTRAINT fk_rating_teacher FOREIGN KEY (teacher_id) REFERENCES sys_user(user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这段 DDL 里有几个设计和建表的关键点。sys_user用role区分三种身份,教师既是登录用户又是课程归属者,所以course.teacher_id指向sys_user,而不是单独建一张教师表。rating_record的核心在最后一行UNIQUE KEY,它从数据库层面保证同一个学生对同一门课程只能提交一次评教,这比在 Servlet 里写逻辑判断可靠得多,可以彻底堵住重复提交的漏洞。
三个外键的设计在课设阶段是有争议的。外键能保证数据完整性,但也会带来一个问题:删除用户时可能会被约束卡住,比如删学生时rating_record里还有记录就删不掉。我的处理策略是保留外键用于答辩讲解和说明关系,但实际开发中可以把外键去掉,只留普通索引。如果选择了保留,测试时就不要去删除有评教记录的用户,否则会不断踩级联删除的坑。
4.2 评教结果统计的 SQL 写法
评教系统的最终交付物是统计报表,核心 SQL 就两种写法。第一种是查某位教师所有课程的平均分。第二种是查全局教师排名。前者用于教师端个人结果展示,后者用于管理员端的汇总页。正确写法如下:
SELECT c.course_name, AVG(r.score) AS avg_score, COUNT(r.rating_id) AS rating_count FROM course c LEFT JOIN rating_record r ON c.course_id = r.course_id WHERE c.teacher_id = 1 GROUP BY c.course_id, c.course_name ORDER BY avg_score DESC;注意这里用的是LEFT JOIN而不是INNER JOIN,原因是如果某门课程还没有学生评教,INNER JOIN会把这门课整个丢掉,教师端页面上就看不到这门课,这是个隐蔽的查漏问题。GROUP BY c.course_id, c.course_name这种写法在 MySQL 8.0 下不会报错,因为course_name功能依赖于course_id,但如果换用 5.7 且开启了ONLY_FULL_GROUP_BY,就一定要把c.course_name也放进GROUP BY。
排名统计自然要处理AVG和ORDER BY的组合。这里有一个容易忽略的坑:平均值默认保留四位小数,展示在 JSP 页面上可能显示成9.5000,想保留一位小数就得在 SQL 里用CAST(AVG(r.score) AS DECIMAL(3,1))或者在 Java 里用BigDecimal格式化。两种方案都行,但放 SQL 里做更简单,JSP 端就不需要写格式化代码了。如果需求是按总评分人数排序再按平均分排序,ORDER BY里加两项:ORDER BY COUNT(r.rating_id) DESC, avg_score DESC,先看谁评得多,再看谁分高。
4.3 用什么手段保障统计效率
评教系统数据量不大,一张rating_record表通常只有几百上千条记录,索引的意义不大。但如果要支撑全校几千学生同时评教,就需要考虑索引和只查一次数据库的优化。前面 DDL 里已经建了idx_teacher_id和联合唯一键,查询WHERE teacher_id=?和WHERE student_id=? AND course_id=?都能命中索引。需要额外注意的,是评教记录表的时间字段rated_at只用于展示和按学期分组,SQL 里尽量用DATE_FORMAT(rated_at,'%Y-%m')做月度统计,不要在这个字段上加函数后查询,否则索引失效。
存储过程在这个系统里可用可不用。如果把“每位教师的平均分排行”写成存储过程,能减少 Servlet 端的 SQL 拼接,但课设阶段维护起来反而麻烦,修改一次逻辑就要重新执行一次脚本。我一般更愿意把统计 SQL 直接写在 DAO 层,以参数形式传teacherId或semester。面试如果被问到存储过程的优缺点,手边正好有这个项目可以当例子讨论。
5. 核心功能实现:从 JSP 登录页到评分完成的三个关键代码块
这一章是真正的动手环节。网上评教系统的页面逻辑比较固定:学生登录后看到待评课程列表,点进任一门课打分写评语,提交后刷新列表状态;教师登录后看到所授课程得分。整个过程拆开来就是登录校验、选课列表、评分提交三件事,对应三个必须写对的代码块。
5.1 JDBC 工具类,所有页面的数据入口
不管用 JSP 写什么页面,都要先拿到数据库连接。最常见的做法是写一个DBUtil工具类,把连接的创建和关闭集中处理。下面是精简但可直接运行的版本:
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/rating_system" + "?useSSL=false&useUnicode=true&characterEncoding=utf8" + "&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai"; private static final String USER = "rating_user"; private static final String PASSWORD = "Rating@2024"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError("MySQL JDBC 驱动未找到"); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { 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) {} } }每个参数在这里都有明确用途,缺一个都会在运行期翻车。characterEncoding=utf8是 JSP 页面写中文评语不乱码的底线;useSSL=false避免 MySQL 8.0 默认连接时做 SSL 握手导致控制台刷警告;serverTimezone=Asia/Shanghai解决 MySQL 8.0 与 JDK 时区不一致导致的日期偏差。如果漏了allowPublicKeyRetrieval=true,连接时会直接抛Public Key Retrieval is not allowed异常,而且这个问题在修改密码加密规则之前是绕不过去的。
这段代码还需要注意的是连接资源的释放。每个 DAO 方法的 finally 里都必须调用close(),不能偷懒。一个 Servlet 请求同时打开两个连接的场景在这个系统里很常见,比如评分页要查询课程信息、又要检查是否已评过,如果连接不关,Tomcat 跑一上午连接池就耗尽,页面会越来越慢直到完全无响应。
5.2 登录拦截与 Session 校验
一个 JSP 网站如果没有登录拦截,那它只是一个页面集合,不是一个系统。网上评教系统分为学生端、教师端、管理员端,三种角色各只能访问自己的功能模块,所以登录后 Session 里的角色身份必须统一用。登录 Servlet 的骨架:
@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String password = req.getParameter("password"); User user = new UserDao().findByUsernameAndPassword(username, password); if (user == null) { req.setAttribute("error", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); return; } HttpSession session = req.getSession(); session.setAttribute("loginUser", user); session.setMaxInactiveInterval(30 * 60); resp.sendRedirect(resolveHomePage(user.getRole())); } private String resolveHomePage(String role) { if ("TEACHER".equals(role)) return "teacher/index.jsp"; if ("ADMIN".equals(role)) return "admin/index.jsp"; return "student/index.jsp"; } }逻辑很简单,但有一个细节常被忽略:登录成功后用sendRedirect而不是forward。原因很简单,如果用forward跳转,学生按 F5 刷新页面时浏览器会重复提交上一次的 POST 表单,轻则弹确认框,重则重复评教。redirect会改变浏览器地址栏,刷新时只会重新请求student/index.jsp,这在 jsp 页面加载完刷新一次的体验上更正常。
第二处细节是密码存储。课设系统直接明文存密文比对也说得过去,但如果想拿得出手,密码字段存 MD5 或 BCrypt 哈希,登录时先把req.getParameter("password")哈希再校验。代码多几行,但能在答辩时讲出“系统安全”的设计思考,比把精力全花在页面样式上高明得多。
5.3 评分提交时的事务处理与并发控制
评教提交这个操作在整个系统中最特殊,它同时涉及写入评教记录和更新课程评教状态两步,必须保证要么都成功要么都失败。很多课设项目在这里不处理事务,导致学生提交时提示失败但成绩已经写进去,重复提交又报唯一键冲突。处理方案是使用 MySQL 事务,把 Service 层方法加上以下逻辑:
Connection conn = DBUtil.getConnection(); conn.setAutoCommit(false); try { RatingDao dao = new RatingDao(); dao.insertRating(conn, rating); dao.updateCourseStatus(conn, rating.getCourseId()); conn.commit(); } catch (SQLException e) { conn.rollback(); throw new RuntimeException("评教保存失败,数据已回滚", e); } finally { conn.setAutoCommit(true); DBUtil.close(conn, null, null); }这个代码块要点是把conn传入 DAO 方法,而不是在 DAO 里自己获取新连接,否则commit和rollback管不到另一条连接上的操作。setAutoCommit(false)表示手动管理事务,两个操作都在同一个物理连接上执行。如果第二步updateCourseStatus失败,第一步的insertRating也会被回滚,不会留下半截数据。这一步值得在项目报告里专门写一段,因为它是评教系统区别于普通 CRUD 页面的核心。
并发方面需要注意的是 MySQL 的锁机制。两个学生同时对同一门课程提交评教,数据库的UNIQUE KEY uk_student_course会挡住第二个请求,但如果先查后插就可能出现竞态。把事务隔离级别保持默认REPEATABLE READ即可,不需要手动加FOR UPDATE。用唯一索引兜底是最省心的做法。
6. 部署避坑与验证:把整个系统打包扔进 Tomcat 的正确姿势
所有代码写完,最后一步是部署。网上评教系统的部署包含两个动作:把 JSP 项目变成可运行的 Web 应用、把 MySQL 数据脚本导入目标数据库。这一步卡住的概率比写代码还高,因为问题往往出在环境而不是逻辑上。
6.1 导出 WAR 包与初始化数据库
用 IDEA 的 Build Artifact 打出rating_system.war,放到 Tomcat 的 webapps 目录,启动 Tomcat 会自动解压。数据库初始化分两步,用 Navicat 或命令行执行都不是问题:
mysql -u root -p < rating_system.sql cp rating_system.war /path/to/tomcat/webapps/ cd /path/to/tomcat/bin && ./startup.shrating_system.sql文件里应该包含建库建表语句和初始用户数据。需要注意的是 SQL 文件本身的字符集,Windows 下用记事本另存为 UTF-8,否则导入时中文注释会变成乱码,影响建表语句的执行。导入再执行SHOW TABLES;检查一遍是不是五张表都在,这一步比后面查代码省时间得多。
6.2 三个高频部署报错的排查顺序
部署阶段问题集中在三个点,遇到的概率几乎是百分之百。
第一个现象:JSP 页面打开白屏或直接报 500,Tomcat 控制台出ClassNotFoundException: com.mysql.jdbc.Driver。原因几乎可以肯定是mysql-connector-java.jar没有放到WEB-INF/lib下。解决方法是把驱动 jar 复制进WEB-INF/lib,重启 Tomcat。如果项目里用了独立 Tomcat 而不是 IDE 内置的,还要确认 IDE 部署的是 exploded war 目录,改 lib 后要重新构建。
第二个现象:页面能登录但所有中文变成?号。原因要分三层排查:MySQL 数据库字符集是 utf8mb4、JDBC 连接串带characterEncoding=utf8、JSP 页面顶部声明pageEncoding="UTF-8"。任何一层没设置都会乱码。从客户看到的?反推,大多数情况是连接串丢了characterEncoding参数,补上重启就好。测试阶段可以用“mysql 常用命令”里的SHOW VARIABLES LIKE 'character_set_%';先确认库层设置。
第三个现象:学生提交第二次评教时报唯一键冲突,界面提示 500 或写入了重复记录。发生原因有两种可能:一是建表时漏了UNIQUE KEY,导致数据库允许重复;二是 Servlet 跳转用了forward而非redirect,刷新时重复提交表单。解决分别对应修改表结构或修改跳转逻辑。这里就是整条链路里最典型的排查顺序:先看控制台报错,再判断是 SQL 层面还是逻辑层面。
验证系统是否跑通,不用看页面多好看,只看一条链路:学生账号登录 → 看到待评课程列表 → 打分提交 → 教师账号登录 → 能看到该门课成绩变化。跑通这一条,剩下都是锦上添花。如果时间有余,可以顺手做一个按学期筛选统计结果的功能,这是评教系统在答辩时最容易引起讨论的点。
整个项目做下来,最深的体感是:这套方案的代码量不大,真正耗时全在环境、编码和部署的细节上。每当我以为“明明都按教程配好了”,最终都会发现某个参数不一致。希望这篇笔记能帮你在同样的坑前面少绕几步,一次跑通。
本文还有配套的精品资源,点击获取