ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

齐鲁文化名人网站:JSP课设项目跑通与二次开发实战

齐鲁文化名人网站:JSP课设项目跑通与二次开发实战 接手这个项目之后我把它完整跑通并翻了一遍代码。这篇博文就还原整个过程从压缩包里的文件结构到环境搭建、数据库导入、Tomcat部署再到业务代码的调用链路最后聊几个可以继续扩展的方向。正在做JSP课程设计、准备毕业设计或者刚学完Servlet/JSP想看看真实项目长什么样的人这篇应该能省你不少时间。1. 压缩包里的“家底”一个JSP课设项目的完整盘算拿到任何项目第一步都是拆包别急着找运行文档。压缩包命名里写了“程序源码数据库调试部署开发环境”实际解压之后里面的东西确实比很多只有源码的压缩包要全得多。简单盘一下核心是这几类内容。1.1 源码目录不是标准Maven结构也没关系项目源码是典型的Java Web工程目录。如果不是用IDEA的Maven骨架建的而是直接创建的传统Web项目目录会长这样qilu/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/qilu/ │ │ │ ├── controller/或servlet/ │ │ │ ├── service/ │ │ │ ├── dao/ │ │ │ └── entity/ │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── web.xml │ │ │ └── lib/jar包 │ │ ├── index.jsp │ │ ├── list.jsp │ │ ├── detail.jsp │ │ └── admin/后台页面 ├── database/ │ └── qilu.sql建库脚本初始数据 ├── README.txt └── qilu.war可选的打包产物如果它带pom.xml那就是Maven结构如果只有web目录和一堆jar包就是传统结构。这两种我都会在下面讲到实际处理起来差别不大关键是知道你的项目属于哪一种。1.2 技术栈一眼看穿JSP Servlet JDBC这个项目没有用Spring、Spring Boot这类框架核心依赖就是Servlet、JSP、JSTL和MySQL驱动可能再加一个连接池。技术选型很“课程设计标准答案”但对学习来说反而是好事因为每一层代码都是公开透明的你能看清一次完整请求从头到尾走过的每一步。技术栈清单如下组件用途常见版本备注JSP页面显示层2.3搭配EL表达式和JSTLServlet请求控制层3.0 / 3.1老项目常用WebServlet注解JDBC/DBUtils数据访问层视驱动而定核心是PreparedStatementMySQL数据库5.7 / 8.0注意驱动类名差异TomcatWeb服务器8.5 / 9.x慎用Tomcat 10IDEA开发环境2020Ultimate版才能建Web项目1.3 功能模块齐鲁文化名人网站到底有哪些功能这类文化名人展示网站功能不会特别复杂但麻雀虽小五脏俱全。齐鲁历史文化名人这个主题选材上通常覆盖山东历史上的代表人物比如春秋战国的孔子、孟子、墨子、管仲、晏婴三国的诸葛亮两晋的王羲之唐宋的颜真卿、李清照、辛弃疾清代的蒲松龄等等。模块上我盘下来基本上是这么几块前台展示首页轮播名人列表列表页是按朝代/类型筛选详情页名人的生平简介、主要成就、代表作品、相关名言后台管理管理员登录名人的增删改查辅助功能分类管理、留言板有的版本有每一块页面之间通过Servlet跳转串联数据全部从MySQL读出来没有写死的静态内容。这一点是这类项目的核心学习价值所在。2. 开发环境组装版本不匹配后面全是坑环境这块是整个项目能否跑起来的关键。我这里直接给一组稳定可靠的版本搭配组合然后逐个说明为什么要这样选。2.1 版本搭配对照表软件建议版本说明JDKJDK 81.8JSP老项目的“安全牌”Tomcat8.5 或 9.0与JDK 8兼容支持Servlet 3.xIDEA2021 / 2022Ultimate版社区版需要额外插件支持MySQL5.7 或 8.0本文按8.0为例注意驱动类名不同MySQL驱动mysql-connector-java 8.0.x5.x和8.x的配置写法有差异为什么强调JDK 8 Tomcat 8.5/9因为很多老项目用的还是javax.servlet包Tomcat从10.0开始把包名整体改成了jakarta.servlet。如果你拿Tomcat 10去跑老项目JSP页面一打开就会报java.lang.NoClassDefFoundError: javax/servlet/...第一反应可能以为代码有问题其实是环境版本不对。我见过太多人在这一步浪费一下午。还有一点要提醒MySQL 8.0的JDBC驱动类名是com.mysql.cj.jdbc.Driver而MySQL 5.x用的是com.mysql.jdbc.Driver写数据库配置文件的时候别搞混。另外MySQL 8.0的JDBC URL建议把时区和SSL参数带上不然连接时会报The server time zone value之类的错误后面数据库配置那节我会详细说。2.2 用IDEA打开项目的正确姿势如果你是第一次接手这种源码项目我建议的操作顺序是这样的。先确认自己的IDEA是Ultimate版。社区版虽然能打开Java项目但对Java Web开发支持不全Tomcat集成、Web插件都是Ultimate才有的。接着把项目解压到不含中文和空格的路径比如D:\projects\qilu千万别放在桌面带中文的文件夹里Tomcat和IDEA对中文路径的容忍度极低。如果你拿到的是Maven工程有pom.xml用IDEA的Open选项直接选择pom.xml打开IDEA会自动识别依赖并拉取jar包。打开后进入Project Structure确认Project SDK设置为JDK 8Language level也跟着设到8。如果是传统工程没有pom.xml但WEB-INF下有lib目录就Open整个目录然后右键项目选择Add Framework Support勾选Web Application把web根目录指到src/main/webapp。这里有个细节很容易踩坑传统工程导入后IDEA有时候不知道web.xml在哪里导致后面配置Artifacts时生不成正确的War包报错信息会是“Artifact without web.xml”。解决办法是到Project Structure - Facets里把Web模块的Web Resource Directory手动指到src/main/webapp目录对应的Web.xml路径指到WEB-INF/web.xml。2.3 Maven依赖还是本地jar包这个项目是传统结构和Maven结构都可能有。如果是传统结构jar包一般在WEB-INF/lib下比如lib/ ├── mysql-connector-java-8.0.27.jar ├── jstl-1.2.jar ├── commons-dbutils-1.7.jar └── druid-1.2.8.jar如果是Maven结构pom.xml里的依赖大概是这样的dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.27/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency /dependencies这里唯一的坑是javax.servlet-api的scope是provided意思是编译时用它但部署到Tomcat时由Tomcat自己提供。如果漏写了provided打出来的War包会自带Servlet API和Tomcat内置的冲突启动时可能出现奇怪的错误。3. 数据库那点事建库、导入、连接池配置与中文乱码根因这个项目的数据库是个ac重要部分。压缩包里通常带一个.sql脚本但很多人拿到脚本不知道怎么用或者导入时报错或者启动后页面显示乱码。我一个个讲清楚。3.1 数据库表设计的长相先看设计。文化名人网站核心表一般就三张起步加上管理员和留言大概是五张表左右。根据我对这类项目的经验表结构大致是下面这样名人大表famous_person字段类型说明idint 主键自增唯一标识namevarchar(50)名人姓名eravarchar(50)朝代如春秋战国、唐代native_placevarchar(100)籍贯如山东曲阜field_typevarchar(50)类型思想家/文学家/军事家/书法家biographytext生平简介achievementtext主要成就famous_wordsvarchar(500)名言image_urlvarchar(200)头像或纪念图路径sort_orderint排序权重管理员表admin_user字段类型说明idint主键usernamevarchar(50)登录名passwordvarchar(100)密码一般MD5再加上朝代分类表、留言表之类的视版本而定。核心还是名人大表和分类表之间的关联。如果你是自己在做类似项目表结构可以直接参考这个设计完全够用。3.2 导入SQL脚本的两种方式拿到.sql文件后在Navicat或MySQL Workbench里右键数据库选择“运行SQL文件”指向qilu.sql即可。这里有个注意点脚本里如果包含创建数据库的语句先看它是CREATE DATABASE还是只建表。如果是只建表你需要先在命令行或者IDE里手动创建库CREATE DATABASE qilu DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后执行mysql -u root -p qilu qilu.sql另一个高频坑是脚本文件本身的编码。如果.sql文件是UTF-8编码而你的MySQL客户端默认是GBK导入后中文全部变成问号。解决办法是导入前在文件头加入SET NAMES utf8mb4;或者在Navicat连接属性里把编码设为UTF-8。这种问题不会直接报错但后面页面上全是“???”排查起来很费劲。3.3 连接池配置与数据库参数的写法项目里的数据库配置文件通常是db.properties或者jdbc.properties放在src根目录下。如果你拿到的是传统项目可能直接写在JDBCUtil工具类里。我建议改成独立的properties文件后续维护方便。标准写法如下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/qilu?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的密码 jdbc.initialSize5 jdbc.maxActive20这里有三处需要注意。一是useUnicodetruecharacterEncodingutf8简写characterEncodingutf8即可它和页面的编码配合解决中文乱码。二是serverTimezoneAsia/ShanghaiMySQL 8.0必须加否则连接时会因为时区不明确报错。三是MySQL 5.x驱动类名要改回com.mysql.jdbc.Driver其他不变。如果你用的是内置DBCP连接池配置会是这样BasicDataSource ds new BasicDataSource(); ds.setDriverClassName(com.mysql.cj.jdbc.Driver); ds.setUrl(jdbc:mysql://localhost:3306/qilu?characterEncodingutf8); ds.setUsername(root); ds.setPassword(123456);其实原理都一样加载驱动、建立连接、复用连接。连接池就是为了避免每次查询都去创建物理连接因为创建连接本身就是最耗时的操作之一。一个访问量不高的课设网站连接池参数用默认值就够但你要明白它存在的意义。3.4 中文乱码的根因与三层排查法乱码问题在JSP老项目里几乎是必现的而且一个地方漏配就会全盘乱掉。乱码本质上是字符编码不一致造成的一共三条链路哪一条断了都会乱。第一层是数据库连接层也就是connection的URL要带characterEncodingutf8让JDBC驱动以UTF-8编码把数据写入MySQL。第二层是页面显示层JSP文件头部要有% page contentTypetext/html;charsetUTF-8 %同时保存文件时IsEclipse/IDEA的文件编码也要是UTF-8否则页面本身内容写出来就是乱掉的。第三层是请求接收层Servlet里要设置request.setCharacterEncoding(UTF-8)否则表单提交的中文姓名会变成问号。凡是遇到中文文字“???”或者显示成菱形乱码的从这三层逐层检查。实在不行就在web.xml里加一个编码过滤器用Spring那个CharacterEncodingFilter会省心很多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如果项目没引入Spring那就自己写一个Filter类名随意doFilter里先设编码再放行逻辑很简单二十行代码搞定。4. 把网站跑起来从IDEA到浏览器的一整条链路环境准备完毕、数据库数据导入完成之后进入最激动人心的环节把Tomcat跑起来在浏览器里看到齐鲁历史文化名人的首页。这一步的完整演示和经典报错排查我放在这一节一起说。4.1 配置Tomcat并部署项目的三种路径IDEA里配置Tomcat先选Run - Edit Configurations然后点左上角加号找到Tomcat Server - Local。首次配置会让你选Tomcat Home指到Tomcat安装目录比如D:\apache-tomcat-9.0.85。然后是部署那一栏。Deployment标签页里点加号选择Artifact。Maven项目会出现qilu:war和qilu:war exploded两个选项。war exploded的意思是“展开的war包”相当于把web目录直接映射到Tomcat修改页面后刷新就能生效开发阶段用这个最方便。部署完再看Application context它是访问路径的根。默认可能是/qilu_war_exploded太长我习惯改成/qilu。这样启动后访问地址就是http://localhost:8080/qilu。注意context如果配置不对页面会404但代码和数据库都没问题这是新手最容易困惑的地方之一。第二种方式是把项目打成war包直接丢到Tomcat的webapps目录。这适合最终交付或者在别的机器上演示的时候。IDEA里Build - Build Artifacts选择qilu:war的Build动作完成会在target目录生成qilu.war。把这个文件复制到Tomcat的webapps下启动Tomcat它会自动解压部署。访问路径同样取决于war包文件名。第三种是传统工程的方式项目右键选择Deployment - Run on Server只要IDE正确识别了Web框架就能直接启动。这三种方式本质一样都是把项目通过Tomcat暴露成HTTP服务区别只是打包和部署的形式。4.2 启动全过程的完整验证清单配好后点启动按钮控制台会滚出一串日志直到看到类似下面这几行才说明启动成功信息: Starting ProtocolHandler [http-nio-8080] 信息: Server startup in [1,250] milliseconds启动之后别急着点页面先做三件事验证访问http://localhost:8080/qilu确认首页能出内容访问http://localhost:8080/qilu/list.do?era春秋战国确认列表页动态数据能查到随便点击一个名人详情确认详情页和图片能正常加载如果首页出来了但列表页数据为空先别改代码回到数据库执行一遍SELECT * FROM famous_person看看表里有没有数据。很多情况下SQL脚本是建表和插入数据合在一起的导入时如果半路报错会导致数据没插完全页面自然就是空的。4.3 高频报错排查表完整链路中的那些坑我整理了一份基于这类JSP项目的高频报错清单每条都是我见过很多次的可以直接对照自查。报错/异常大概率原因解决方案404 Not Found访问路径不对或web.xml中Servlet映射没匹配检查Application context检查Servlet的WebServlet注解路径java.lang.NoClassDefFoundError: javax/servlet/Tomcat版本为10包名迁移换成Tomcat 9或把代码中的javax改成jakartaClassNotFoundException: com.mysql.jdbc.Driver驱动版本不匹配换成com.mysql.cj.jdbc.Driver或确认jar包在lib下Access denied for user rootlocalhost数据库密码错误检查db.properties里的账号密码Server time zone value span异常MySQL 8.0时区问题jdbc.url添加serverTimezoneAsia/Shanghai无法启动端口8080被占用其他Tomcat/服务占用了端口命令行netstat -ano查占用改Tomcat端口或结束占用进程The server is unable to load a web applicationweb.xml配置有误或jar包冲突查看catalina.log详细堆栈重点检查Filter、Listener页面空白或500JSTL找不到标签库pom.xml里要加jstl依赖页头要写taglib指令表单提交后中文乱码request编码未设置Filter统一设置UTF-8这里面最隐蔽的是JSTL的问题。很多老项目的列表页长这样% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %如果lib目录里没有jstl.jar和standard.jar页面会直接500而且Tomcat日志里报的错误可能模模糊糊比如提示“Unable to find tag library”。解决办法就是补依赖。Maven里加上jstl 1.2传统工程就把两个jar放去WEB-INF/lib目录。4.4 调试技巧日志、打印和断点三件套如果页面有数据但显示不对不要干瞪眼看页面按这三步来排查。第一看Tomcat的Console日志和logs目录下的localhost.log / catalina.out异常堆栈基本都会打在这里。第二在Servlet和DAO的关键位置加System.out.println()把接收到的参数、拼出来的SQL、查询到的结果集数量全打印出来。老项目没有Logback也没有lombok这是最快的方式。第三如果IDE环境允许直接在DAO方法里打断点F7单步跟入看ResultSet里到底有没有值以及每个字段的取值。这三个方法从粗到细能覆盖九成以上的问题。别一上来就怀疑代码写错了先用日志确认数据流走到了哪一层。5. 齐鲁名人业务代码的心法从JSP页面到数据库的完整链路项目跑起来只是第一步真正值钱的是看懂代码里的一次完整请求。以“点击某个名人查看详情”为例我把这条链路拆开讲透。5.1 一次请求是如何流转的页面地址栏输入http://localhost:8080/qilu/detail.do?id3Tomcat收到请求后先根据.do后缀和web.xml的配置找到对应的Servlet。要么是web.xml里写了servlet-mapping servlet-namePersonServlet/servlet-name url-pattern*.do/url-pattern /servlet-mapping要么是Servlet类直接用了注解WebServlet(/detail.do) public class PersonDetailServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 接收参数 String id req.getParameter(id); // 2. 调用Service/DAO拼接JDBC查询 PersonDao dao new PersonDao(); FamousPerson person dao.findById(Integer.parseInt(id)); // 3. 把数据塞进request域转发给JSP req.setAttribute(person, person); req.getRequestDispatcher(/detail.jsp).forward(req, resp); } }这里面体现的是标准MVC思想Servlet当控制器负责接参、调服务、分发页面DAO负责数据访问JSP只负责显示数据不写业务逻辑。注意第3步用的是forward而不是redirect因为forward是服务端内部转发这种方式能把request里存放的数据原封不动交给detail.jsp去渲染。如果用redirect相当于让浏览器重新发起一次新请求之前塞进request里的属性全部丢失detail.jsp页面拿到的就是null。5.2 DAO层与PreparedStatement的正确写法DAO层的核心方法长这样public FamousPerson findById(int id) { String sql SELECT * FROM famous_person WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { FamousPerson p new FamousPerson(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setEra(rs.getString(era)); p.setAchievement(rs.getString(achievement)); return p; } } } catch (SQLException e) { e.printStackTrace(); } return null; }这里有几个很好的习惯是这个项目代码里值得学习的。一是用PreparedStatement而不是简单字符串拼接因为在SQL里拼参数会有SQL注入风险课程设计项目虽然不要求高安全性但规范就是规范这个判断和习惯能延伸到以后工作项目。二是用try-with-resources自动关闭Connection、PreparedStatement、ResultSet这样不用手动在finally里写一堆close也避免连接泄露。三是ResultSet取字段用列名而不是下标后续如果加字段代码改动量最小。你可能会问Service层是干嘛的。如果项目分了三层Service代码会写业务逻辑比如“点击量1”“判断是否重复上榜”这种DAO就只管纯粹的数据增删改查。如果项目只有ServletDAO两层那也可以接受毕竟课设规模在那里控制层直接调用DAO不会造成太大问题。5.3 JSP显示层EL和JSTL的配合detail.jsp面的显示几乎可以无脑用EL表达式加JSTL。整个页面的核心就几行% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html body h1${person.name}/h1 p朝代${person.era}/p p类型${person.fieldType}/p p生平${person.biography}/p p成就${person.achievement}/p p名言${person.famousWords}/p /body /htmlEL表达式${person.name}实际上就是调用person.getName()方法。用这种方式的一大好处是相较于%request.getAttribute(person).getName()%要简洁得多而且如果person为nullEL会显示空字符串而不是抛NullPointerException页面友好很多。列表页用c:forEach遍历c:forEach items${personList} varp div classcard h3${p.name}/h3 p${p.era} · ${p.fieldType}/p a hrefdetail.do?id${p.id}查看详情/a /div /c:forEach这套写法放到任何老JSP项目里都通用理解一次以后接手类似项目都能快速上手。5.4 后台管理登录、新增、编辑、删除这一整套后台管理部分也不是很复杂的东西。登录的思路就是表单提交账号密码Servlet查admin_user表查到了就session.setAttribute(adminUser, user)然后转发到后台首页查不到就返回登录页并提示“用户名或密码错误”。有的版本会做MD5加密再比对有的直接明文比看原始项目程度有加密总比没有好。新增和编辑是用同一个JSP页面区别在于有没有默认值。进入编辑页时Servlet根据id查出该名人的信息存到request域JSP里用value${person.name}回填这样新增和编辑能复用一套UI。删除就更直接DELETE FROM famous_person WHERE id?执行完重定向回列表页。整个后台做下来你会发现所有操作的套路都一致接参数、拼SQL、执行、跳转。把一次完整的增删改查走通之后做任何类似的管理系统都没有障碍了。齐鲁历史文化名人网站这个项目最大的价值就在这里它不是花哨的框架组合而是能把基础链路练扎实。6. 拿到源码之后的二次开发搜索、分页、前端升级项目跑通、代码看懂之后如果还要交作业或者想拿这个项目做扩展以下这几个方向是最常见也最实用的。6.1 给列表页加一个搜索框需求很简单在列表页搜孔子、搜“家”之类的关键词或者按朝代筛选。后台实现的思路是在原有list Servlet里增加一个keyword参数public ListFamousPerson search(String keyword) { String sql SELECT * FROM famous_person WHERE name LIKE ? OR biography LIKE ?; String like % keyword %; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, like); ps.setString(2, like); try (ResultSet rs ps.executeQuery()) { // 遍历封装 } } return list; }页面端在列表顶部加一个输入框和按钮提交到同一个list.do后端根据是否有keyword参数决定调用哪种查询。注意LIKE语句中的百分号要拼在参数里而不是拼在SQL语句里不然PreparedStatement预编译的意义就没了。6.2 分页查询的实现名人数量多了一页显示所有会显得又长又慢。分页的逻辑不算难关键是用好LIMITint pageNum Integer.parseInt(request.getParameter(pageNum)); // 当前页默认1 int pageSize 8; // 每页8条 int offset (pageNum - 1) * pageSize; String sql SELECT * FROM famous_person ORDER BY sort_order LIMIT ? OFFSET ?;总条数用SELECT COUNT(*) FROM famous_person取到页数用(total pageSize - 1) / pageSize算出来。然后页面上加“上一页”“下一页”和“第X页/共Y页”的链接点下一页时把pageNum传过来。整套逻辑大概20行代码但实战中非常常用值得动一次手。6.3 换一套现代前端样式老项目的CSS样式可能是纯手写内联样式页面观感大概停留在“能看”的级别。要把颜值提上去最快的办法是引入Bootstrap或Layui的CDN。比如列表页的卡片布局link relstylesheet hrefhttps://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css然后把原来的table布局改成卡片、栅格布局详情页改成两栏图文布局后台上传的图片也能以更规整的方式显示。换样式不会影响任何Java后端的逻辑因为JSP页面最终生成的还是HTMLServlet和数据层完全不关心页面长什么样这一层面的细节。这也正是JSP项目适合做二次开发的原因改哪一层都很独立。6.4 向框架项目迁移的思路如果未来有下一步学习计划可以考虑把这个项目用Spring Boot重写一遍。迁移思路不复杂名人大表的实体类直接复制DAO改成MyBatis的Mapper接口Servlet改成ControllerJSP可以继续沿用Spring Boot支持JSP但需要额外配置或者直接用Thymeleaf替代。迁移一遍的过程会逼你重新理解框架是如何封装Servlet那些重复劳动的。到那时候再回头看这个JSP项目你会发现自己对请求转发、请求域、转发和重定向这些概念的理解扎实很多这是直接上手Spring Boot的初学者不具备的底层感知。我在实际跑这个项目的时候最有感触的一点是越是不起眼的老技术栈越能把内容沉淀出来。这个齐鲁历史文化名人网站技术面覆盖了环境、数据库、部署、前后端交互单看任何一个点都不难但串在一起就是一个完整的Web项目闭环。拿到项目的朋友别急着找人帮忙跑通先静下心按照环境对齐、数据库导入、部署启动、代码分析这个顺序走一遍每解决一个问题你对Java Web的整体理解就会牢实一分。
返回列表