说句不太客气的话:市面上JavaWeb教程一大堆,但"保姆级"这三个字真正配得上的没几个。多数教程默认你机器上什么都装好了,默认你知道什么是Maven,默认你能看懂一长串报错。可真到了零基础同学那里,光是一个Tomcat启动失败就能卡一整天,更别说把数据库接进来了。我这篇不玩虚的,就按真实操作顺序,从环境版本怎么匹配、项目骨架怎么建、war包怎么部署、数据库怎么连,一直讲到前后台怎么打通、页面报错怎么查,全程按"照着做就能跑通"的标准来写。适合刚学完Java语法、准备做第一个Web项目的入门者,也适合折腾了半天总在某个环节反复失败的"夹生"选手。
1. 动手前先把环境"版本组合"定死:JDK、Tomcat、Maven的匹配关系
很多新手遇到的第一堵墙,不是代码问题,而是环境版本之间互相不认。Tomcat启动一闪而过、控制台报UnsupportedClassVersionError、Servlet注解死活扫不到,十有八九是版本组合乱了。这个事得在最开始就定下来,不然后面全白搭。
1.1 三个版本到底谁说了算
先说JDK和Tomcat的关系。你本机装的JDK大版本,决定了Tomcat能不能在当前环境里跑起来。Tomcat本身是Java程序,它需要JVM来加载,所以JDK版本必须高于或等于Tomcat要求的最低版本。举个例子:
| Tomcat版本 | 最低JDK版本 | 注意点 |
|---|---|---|
| Tomcat 8.5 | JDK 7+ | 老项目常见,Servlet规范3.1 |
| Tomcat 9.0 | JDK 8+ | 目前最稳妥的组合 |
| Tomcat 10.0 | JDK 11+ | 包名从javax换成jakarta,老代码会踩雷 |
| Tomcat 10.1 | JDK 11+ | 同上,jakarta命名空间 |
这里有个新手特别容易掉进去的坑:Tomcat 10及以上版本已经全面切换到了jakarta.servlet包名,你要是拿网上搜到的老教程代码,还在写javax.servlet.http.HttpServlet,编译直接报红,运行直接404或者500。所以我的建议非常直白:入门阶段就选Tomcat 9.0.x配上JDK 8,这是目前网上资料最全、踩坑最少、搜报错最容易命中答案的版本组合。等你把Servlet、JSP、Filter这些基础玩明白了,再考虑升级到Tomcat 10体验新命名空间。
Maven的版本也不能随便。Maven本身需要JDK运行,如果你用JDK 8,那Maven 3.6.3左右就够了;如果你上到JDK 17,Maven最好用3.9.x,太老的Maven版本在解析依赖时会因为新JDK的模块限制报一堆奇怪的警告。不过你要是直接在IDEA里用自带Maven,IDEA版本会帮你选一个配套的Maven,这个环节就不用自己操心了。
1.2 我推荐的固定版本组合
为了让这篇博文里所有操作都能复现,我直接把我一直在用的这套组合列出来:
- JDK:jdk1.8.0_202(或者任何一个8u2xx版本)
- Tomcat:9.0.83或更高的小版本
- Maven:3.6.3(或者直接用IDEA内置)
- IDEA:2019.3以上的版本都行,越新越省事
- MySQL:5.7或8.0均可,我下面按8.0来讲,因为新装的机器基本都是8.0了
- 数据库驱动:mysql-connector-java 8.0.33(严格来说现在叫
com.mysql:mysql-connector-j,但老坐标还能用)
为什么我坚持JDK 8 + Tomcat 9?因为这套组合覆盖了绝大多数教材和博客案例,你能搜到的问题解决方案基本都基于这个环境。用太新的环境,比如JDK 21加Tomcat 11,确实先进,但你在网上搜到的十篇教程里可能有九篇写的包名和配置跟你对不上,对零基础来说这是灾难。
装完以后,记得配环境变量。Windows上把JAVA_HOME指向JDK安装目录,把%JAVA_HOME%\bin加到Path里。Tomcat的启动脚本依赖JAVA_HOME,不配的话点startup.bat会闪退。验证方式很粗暴:命令行敲java -version能看到版本号,再敲catalina version能看到Tomcat版本,两个都能正常输出,环境这关就算过了。
2. 创建项目骨架:为什么JavaWeb项目一定要是war包
环境搞定之后,进入正题——建项目。这一步的困惑通常不是"怎么点",而是"我明明按视频来了,怎么结构跟视频不一样"。原因很简单:很多教程用旧版IDEA,新版IDEA的Maven Archetype生成逻辑变了,目录结构长得不一样。
2.1 从IDEA创建Maven Web项目的完整路径
我建议直接走Maven Archetype这条路,别用IDEA自带的Web Application模板。Maven项目的好处是依赖管理明确,后面加数据库驱动只用改pom.xml,不用手动下载jar包往WEB-INF里塞。
操作路径:New Project → 选Maven → 勾选Create from archetype→ 选择maven-archetype-webapp。这个Archetype生成的项目结构是最基础的:
项目名/ ├── pom.xml └── src/ ├── main/ │ ├── resources/ │ └── webapp/ │ ├── WEB-INF/ │ │ └── web.xml │ └── index.jsp └── test/注意一个特别容易让人懵的地方:这个模板默认没有src/main/java目录。很多新手建完项目,想创建一个包,左看右看找不到java目录,还以为是IDEA坏了。其实只要自己手动在main下面新建一个java目录,IDEA会自动把它识别成源码根目录,图标变蓝就对了。
pom.xml里要确认几件事。第一是打包方式:
<packaging>war</packaging>这一行决定了Maven最终构建出来的产物是war包,而不是可执行的jar包。很多同学上来就把这个忘了,或者不知道要写,导致package之后生成的是个没法部署的jar。第二是把Servlet API依赖加上,scope用provided:
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>这个provided是精髓。它的意思是:编译的时候Servlet API在,但打包的时候不把jar塞进war包,因为Tomcat自己已经带了一套Servlet实现。你要是不用provided直接默认compile,打出来的war包会和Tomcat自带的Servlet类冲突,轻则告警,重则出现方法签名不一致的诡异报错。
2.2 war与jar的本质区别
为什么JavaWeb项目必须打成war包?这得从两种包的结构说起。jar包是Java应用或库的归档格式,里面有META-INF/MANIFEST.MF,如果你写了一个带main()入口的普通程序,打成jar可以靠java -jar直接运行。但Web应用不是这么启动的,它没有main方法,它需要被Servlet容器(也就是Tomcat)加载后才能对外提供服务。
war包的结构是特殊的,它必须有WEB-INF目录,这个目录下面至少要有web.xml,WEB-INF/classes放编译后的class,WEB-INF/lib放依赖的jar。Tomcat拿到war包后,把它解压到自己的工作目录,读取WEB-INF/web.xml,扫描注解,实例化Servlet,然后才开始监听端口。这个机制也解释了前面的provided为什么成立:Tomcat启动时能加载Servlet API,是因为它自己的lib目录里有实现,而不是靠你的war包带进去。
很多教程会让新手看一眼web.xml,我建议你也看,因为这是Web应用的入口说明书。一个最精简的web.xml长这样:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <display-name>MyWebApp</display-name> </web-app>Servlet 3.0以后,可以不写Servlet映射配置,用@WebServlet注解直接搞定。但web.xml这个文件本身要保持存在,空壳也行,很多服务器会检查它。
3. Tomcat部署的两条路:手动部署的完整流程与底层逻辑
项目结构建好了,代码暂时还是最基础的index.jsp,这时候就可以先部署一次,把"开发→打包→部署→访问"这条链路跑通。这一步你跑通的越早,后面写功能就越心里有底。
3.1 将war包丢进webapps:部署命令与验证方式
先讲最原始也最透明的方式:Maven打包,把war丢进Tomcat的webapps目录。
在IDEA右侧的Maven工具窗里,双击clean再双击package,然后看target目录,里面会生成一个项目名-1.0-SNAPSHOT.war。找到Tomcat安装目录下的webapps文件夹,把这个war复制进去,再启动Tomcat。
启动方式:Windows下双击bin/startup.bat,Linux/Mac下执行bin/startup.sh。启动后控制台或者logs/catalina.日期.log日志里会打出一串消息,其中有一行类似:
Deployment of web application archive [xxxx.war] has finished看到这行就说明部署成功了。然后浏览器访问http://localhost:8080/项目名/,能看到页面的输出。
这里有个新手几乎必踩的认知坑:访问路径里的"项目名"到底是什么。它默认取war包文件名。比如你的war叫demo-1.0-SNAPSHOT.war,那访问地址就是http://localhost:8080/demo-1.0-SNAPSHOT/,一个字符都不能差。现在你明白为什么很多项目喜欢把war重命名成ROOT.war了吧?因为ROOT.war对应的是根路径,访问http://localhost:8080/就能直接打开。不过入门阶段我不建议你管这个,能访问到就说明链路通了,路径问题后面自然懂。
3.2 部署不生效?work目录缓存与清理
Tomcat部署后不是直接把war文件当文件夹用,它会解压war到webapps下生成同名的目录,后续就从这个目录读资源。如果你改动class文件或者JSP,有时候会感觉"明明改了,浏览器还是老样子",这时候得检查两个地方。
第一是浏览器缓存,按Ctrl+F5强制刷新;第二是Tomcat的work/Catalina/localhost/项目名目录。Tomcat会把JSP编译成Servlet的class文件放在这里,如果你改了JSP而Tomcat没重新编译,就会读到旧版本。停掉Tomcat,把work目录清空再启动,基本都能解决。
顺带提一个部署时容易踩的坑:直接往webapps里复制war的时候,如果Tomcat还在运行,可能会出问题。稳妥的流程是:停Tomcat → 删旧包和旧解压目录 → 放新war → 启动。很多教程说可以热部署,那是生产环境配了管理账号才敢干的,本地练手别学那些花活。
如果你想省事一点,IDEA里可以直接配置Tomcat Server,通过Deployment选项卡把war包和启动脚本关联起来,点一下绿色箭头Tomcat自动启动并部署。这个方式开发时很方便,但我不建议你入门阶段完全依赖它,因为IDEA帮你把底层细节全部藏掉了,一旦出问题你无从下手排查。手动部署一轮之后,再用IDEA集成就是水到渠成的事。
4. 数据库连接的三道坎:驱动、URL、代码结构
部署链路通了以后,正戏来了——让Web应用和MySQL对话。这一步涉及三个层面的问题,任何一层出问题,报错都足够让人怀疑人生。
4.1 MySQL驱动选择:为什么不建议用旧版驱动
Java程序连MySQL靠的是JDBC驱动,在Maven里加依赖就开始引入。我这里强烈建议:别再用5.1.x版本的驱动了,哪怕你的MySQL是5.7。旧版本驱动对于MySQL 8.0默认的caching_sha2_password认证插件支持不完整,会报Public Key Retrieval is not allowed之类的错误,虽然能通过URL参数救回来,但何必给自己找麻烦。
MySQL 8.0对应的驱动坐标:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>注意驱动类名也变了。老驱动是com.mysql.jdbc.Driver,新驱动是com.mysql.cj.jdbc.Driver。代码里写Class.forName的时候别写错,新版驱动即使不写Class.forName也能自动注册,但写了也不报错,所以我建议写上,兼容性更好。判断你有没有写对,一个最简单的验证方式:单独跑一个带有main方法的测试类,能连上就万事大吉,连不上先把报错解决,再往Web项目里写。
4.2 JDBC URL参数详解:serverTimezone、useSSL到底干什么
连接串是新手最容易复制错的地方,因为它太长了。一个标准的MySQL 8.0连接串:
jdbc:mysql://localhost:3306/yourdb?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8每个参数都不是白写的,逐个解释一下:
useSSL=false:本地开发没有配置SSL证书,MySQL 8.0默认会尝试SSL连接,不加这个参数,控制台会刷出Establishing SSL connection without server's identity verification is not recommended这样的警告,严格模式下甚至报错。serverTimezone=Asia/Shanghai:MySQL 8.0对时区校验变得严格了,数据库和驱动之间的时区不一致时,会直接抛The server time zone value 'Öйú±ê׼ʱ¼ä'这类乱码报错。显式指定时区就是把这个坑提前填掉。characterEncoding=utf8:让驱动以UTF-8编码和数据库通信,配合表结构的utf8mb4,中文才不会变问号。
先不管代码怎么写,你可以先用命令行或者可视化客户端确认一件事:yourdb这个数据库存在吗?表建了吗?很多报错根本不是Java侧的,而是数据库本身空荡荡,Java程序连上了却找不到表,然后报Table doesn't exist。我建议先在MySQL里把环境准备好:
CREATE DATABASE IF NOT EXISTS yourdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE yourdb; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); INSERT INTO user (username, password) VALUES ('admin', '123456'), ('zhangsan', 'abcdef');必须特意强调一下utf8mb4,它是MySQL 8.0的默认字符集,也是能完整支持中文和emoji的字符集。用老的utf8字符集建表,中文勉强够用,但遇到生僻字还是会出问题。
4.3 JDBC连接代码的标准写法与资源释放
连接数据库的代码写起来不难,难的是养成好习惯。新手最常见的毛病是:连接用完了不关,一会儿数据库就报Too many connections。后面排查的时候又不知道哪来的连接。
我建议一上来就写工具类,把获取连接和关闭资源封装起来,避免每个地方都重复写一大坨:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JdbcUtils { private static final String URL = "jdbc:mysql://localhost:3306/yourdb?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你自己的密码"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException ignored) {} } if (stmt != null) { try { stmt.close(); } catch (SQLException ignored) {} } if (conn != null) { try { conn.close(); } catch (SQLException ignored) {} } } }顺手测一下这个工具类,写一个main方法,查一下刚才建的表里有没有数据。这一步测试通过了,数据库侧就稳了。以后Web程序里报数据库错误,起码能排除"驱动没下载"和"连接串写错"这两个最基础的原因。
5. 把前后台真正打通:一个用户列表页的完整链路
数据库连接通了,但光在main方法里查数据没意思,要让用户在浏览器里能看到数据库里的内容,这才算完整的Web应用。我拿"用户列表"这个最典型的需求走一遍链路:JSP做页面,Servlet接收请求,JDBC查数据,最后在页面上渲染出来。
5.1 用DAO把数据库操作隔离出来
也许你会觉得,查个用户列表而已,直接在Servlet里写JDBC不就完了?我的建议是:哪怕这个项目再玩具,也把数据库操作单独放一层。因为一旦你把SQL和JDBC代码混在Servlet里,后期只要加一个功能,Servlet就会膨胀得没法看,你会陷入无边无际的复制粘贴。
最简单的设计,一个实体类加一个DAO类就够了:
public class User { private int id; private String username; private String password; private Date createTime; // getter和setter,自己补全,IDEA里Alt+Insert可以自动生成 }import java.sql.*; import java.util.ArrayList; import java.util.List; public class UserDao { public List<User> findAll() { List<User> list = new ArrayList<>(); String sql = "SELECT id, username, password, create_time FROM user ORDER BY id"; try (Connection conn = JdbcUtils.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setCreateTime(rs.getTimestamp("create_time")); list.add(user); } } catch (SQLException e) { throw new RuntimeException(e); } return list; } }try-with-resources是Java 7以后的语法,它保证连接、Statement、ResultSet在语句结束时自动关闭,比手动close干净很多,也不会出现漏关连接的问题。入门阶段我不推荐一上来就学连接池,先把最原始的链路跑通,连接池那些只是锦上添花。
5.2 Servlet中用forward转发数据给JSP
Servlet这一层做的事情非常简单:调用DAO,拿到List,放到request里,再转发给JSP展示。
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @WebServlet("/user/list") public class UserListServlet extends HttpServlet { private final UserDao userDao = new UserDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<User> users = userDao.findAll(); req.setAttribute("users", users); req.getRequestDispatcher("/list.jsp").forward(req, resp); } }这里有一个初学者很容易混淆的知识点:为什么用forward而不是sendRedirect?因为forward是在同一个请求内部把处理权交给另一个组件,request里存的属性还能接着用,JSP可以直接读取。而sendRedirect是让浏览器重新发起一次新的请求,原来的request数据全丢。你要是在sendRedirect之后试图用EL表达式取值,得到的一定是空。
5.3 JSP页面用EL和JSTL遍历数据
JSP页面里不建议写Java代码,虽然教条了点,但这是真的为你好。用EL表达式加JSTL标签,代码干净不说,后期维护改样式也方便。
先修改web.xml上面说的pom.xml,加入JSTL依赖。如果是Tomcat 9这类基于javax的版本,可以用:
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>然后新建list.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>用户列表</title> </head> <body> <h3>用户列表</h3> <table border="1"> <tr> <th>ID</th> <th>用户名</th> <th>创建时间</th> </tr> <c:forEach items="${users}" var="u"> <tr> <td>${u.id}</td> <td>${u.username}</td> <td>${u.createTime}</td> </tr> </c:forEach> </table> </body> </html>部署后再访问http://localhost:8080/项目名/user/list,能看到三行数据(admin、zhangsan和后面的测试数据),整条链路就算完整打通了。
如果需要带上用户密码字段,记得在JSP里别直接显示,真实的项目里密码绝不会以明文形式摆在页面上。这个话题属于安全的范畴,但现在可以先有个概念。
5.4 处理中文乱码的正确姿势
做Web开发绕不开乱码,尤其是POST请求提交中文、GET请求带中文参数、响应输出中文三种场景,处理方式各不相同。
先说响应乱码。在你Servlet里处理业务之前就设置编码:
resp.setCharacterEncoding("UTF-8"); resp.setContentType("text/html; charset=UTF-8");setCharacterEncoding通知容器用UTF-8编码输出字符,setContentType通知浏览器用UTF-8解析页面,两个一起用才保险。
然后是请求乱码。Tomcat 8以上版本,GET和POST请求默认UTF-8解码,一般不会乱。但你要是写Filter统一处理会更稳,这里给一个最经典的字符编码Filter写法:
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class CharacterEncodingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } }@WebFilter("/*")表示拦截所有请求。有了这个Filter之后,你所有的Servlet基本都不用再单独设置编码。我把这个Filter的代码写在前面,你新建一个类粘进去就能用。
提示:数据库那边也要保证字符集是utf8mb4,连接串里也要有characterEncoding=utf8,Java侧、数据库侧、页面侧三处编码对齐,中文才不可能乱。
6. "部署成功但页面报错"的通用排查链路
走到这一步,你已经能跑通一个带数据库的完整Web应用了。但这就像开车,会开还不够,得会排除故障——我敢打赌,你后面写代码遇到的报错,会比成功的瞬间多得多。所以我把最容易踩的几个坑按"从外到内"的顺序整理一遍,这些几乎能覆盖新手阶段九成的问题。
6.1 404:请求路径不对还是部署不对
浏览器看到404,第一反应不是改代码,而是分情况判断。404分两种:一种是整个应用都访问不到,一种是应用能访问但某个路径不对。
整个应用都访问不到,先看Tomcat有没有部署成功。浏览器访问http://localhost:8080/如果能出现Tomcat默认首页,说明服务器本身是好的,问题出在你的应用上,去确认war包是否成功部署、访问的路径是不是war包解压后的目录名。
应用能访问首页,但点某个链接就404,这说明Servlet映射有问题。用注解方式就检查@WebServlet("/xxx")里的路径,注意路径必须以斜杠开头,而且你浏览器里访问的地址是不是http://localhost:8080/应用名/xxx。用web.xml配置就检查url-pattern写的是不是/xxx,以及servlet-mapping里的servlet-name和servlet里的是不是完全一致。
还有一个隐藏点特别阴:你建了Servlet类,也写了注解,但忘记把它放到src/main/java目录下,而是误放到了src/main/resources里。那样编译后class不会进到war包的WEB-INF/classes的正确位置,Tomcat扫描不到,结果就是404。遇到404先别急着改代码,去war包里翻一翻,看看WEB-INF/classes下面到底有没有你那个Servlet的class文件。
6.2 500且日志出现ClassNotFoundException:依赖没跟着进war包
页面直接报500或者白屏,Tomcat日志里有ClassNotFoundException: com.mysql.cj.jdbc.Driver,这是最常见的数据库驱动没打包进去的典型症状。你在pom.xml里明明写了依赖,为什么部署后没有?
最常见的原因是scope。比如Servlet API你写了provided,那是OK的,因为Tomcat自带。但MySQL驱动如果你也图省事抄了个provided,那就坏了——provided的依赖不会进war包,运行的时候Tomcat当然找不到驱动类。记住:mysql-connector-java的scope要么不写(默认compile),要么写成runtime,就是不能写成provided。
另一个原因是IDEA的Artifacts配置问题。老版本IDEA里,Maven依赖要单独手动添加进WEB-INF/lib。检查路径:Project Structure → Artifacts → 选中你的war配置 → 看Available Elements里有没把依赖带进去。新版IDEA基本都自动化了,但如果你用的版本比较老,这一步还是要看一眼。
排查的时候别看浏览器,直接看Tomcat的logs。我给你一个判断小技巧:看到ClassNotFoundException,定位到出问题的是哪个类;如果是com.mysql开头的,去target/项目名/WEB-INF/lib目录里找有没有对应的jar;没有,就回pom.xml查scope和有没有刷新依赖。
6.3 数据库连接失败的三大高频原因
最后单列数据库连接问题,因为这是最接近"后端程序员日常"的一类报错。连接失败最常见的三类原因,每个都能对号入座:
连接串或账号密码错了典型报错是
Access denied for user 'root'@'localhost'。这种错误通常不是密码改过,就是你在JdbcUtils里把URL、USER、PASSWORD写的是假数据。连本地数据库,主机用localhost还是127.0.0.1都行,但端口看看MySQL实际监听的是不是3306。MySQL服务根本没启动报错是
Communications link failure或者Connection refused。Windows下用netstat -ano | findstr 3306看看端口有没有在听,没在听就去服务管理里启动MySQL服务。很多人Tomcat没问题、代码没问题,结果数据库没开,折腾半天查不到原因,其实是一巴掌的事。驱动类找不到报错是
ClassNotFoundException或者No suitable driver found。前者说明jar没进war包,按上面第6.2节查;后者往往是连接串格式写错了,比如jdbc:mysql://写成了mysql://,或者URL里主机名和数据库名之间少了斜杠。多看两遍,错别字问题占了很大比例。
为了帮你省事,我把上面说的常见报错和原因放到一张速查表里,遇到问题直接查:
| 报错关键字 | 最可能的原因 | 处理方向 |
|---|---|---|
| HTTP 404 | 访问路径不对、Servlet没被扫描到、class没编译进war | 检查URL路径和war包内容 |
| HTTP 500 | 运行时代码异常、依赖缺失 | 看日志定位具体异常栈 |
| ClassNotFoundException: com.mysql.cj.jdbc.Driver | 驱动jar没进WEB-INF/lib | 检查scope和Artifacts |
| Access denied for user | 用户名密码错误 | 核对数据库账号 |
| Communications link failure | MySQL服务没启动或端口不通 | 启动服务、检查端口 |
| Table doesn't exist | 建表脚本没执行或数据库选错 | 检查SQL和数据库名 |
| Server returns invalid timezone | 连接串缺serverTimezone | 补上serverTimezone参数 |
| Too many connections | 连接没关闭 | 检查代码资源释放 |
我见过太多同学遇到报错第一件事就是百度搜"解决方案",然后把网上乱七八糟的配置往自己项目里堆,越堆越乱。其实解决问题的正确姿势永远是:先看日志,找到第一次出现异常的那行,从那里开始查。Tomcat的日志路径在logs目录下,localhost.日期.log记录了Web应用自身的异常,catalina.日期.log记录容器层面的信息。你耐着性子把日志从头读到尾,大部分问题的答案都已经写在里面了,根本不用求人。
最后再分享一点个人的带人经验。这套环境组合我让无数个零基础的同学跑通过,反复踩的也就是这二十个坑。如果你照着这篇文章做到某一步卡住了,先别急着怀疑自己能力,回头检查版本组合和依赖配置——十有八九是把某个依赖的scope抄错了,或者把Tomcat 10的教程用在Tomcat 9上了。JavaWeb入门真的不难,难的是你愿意打开日志、愿意去war包里翻class文件。能把这两件事做好,你已经超过身边一大半同学了。