news 2026/9/26 18:13:31

JavaWeb_01项目拆解:Servlet+JSP+JDBC+MySQL完整入门实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb_01项目拆解:Servlet+JSP+JDBC+MySQL完整入门实践

我当年第一次跑通“JavaWeb_01”这个项目的时候,大概花了一个周末加两个晚上。不是代码难写,而是环境、路径、乱码这些问题轮着来,任何一个地方卡住,新手都容易直接心态崩溃。这个项目本身不复杂——一个基于Servlet + JSP + JDBC + MySQL的用户管理小系统,或者说一个还没上Spring Boot的“古典派”JavaWeb项目,但它几乎是所有Java后端入门必经的一条路:你绕不开HTTP请求处理、绕不开数据库连接、绕不开MVC分层,也绕不开Tomcat部署,而这些恰恰在Spring Boot里都被“藏”起来了,你平时根本意识不到它们的存在。

这篇博文我想把“JavaWeb_01”从头到尾拆开讲一遍,包括项目该怎么设计、IDEA里怎么配Tomcat、数据库表和JDBC代码怎么组织、JSP页面怎么传值、以及我踩过的那些坑。不光是给新手抄作业,更多是把每个步骤背后为什么要这么做讲清楚。适合刚学完Java SE、正准备做第一个Web项目的人,也适合那种跟着黑马视频敲完一遍、但代码好像是自己跑起来又好像没完全跑起来的人。看完你能对“一个JavaWeb项目到底由哪几块组成”有完整的画面感。

1. 项目拆解与整体设计思路

1.1 先把需求说清楚:这个项目究竟在做什么

“JavaWeb_01”听起来像是一个随手起的项目名,实际就是一个典型的“用户管理”入门系统,功能不外乎三个:登录、列表展示、增删改查。你查网上的完整案例也好,照着教程做也好,底层都是这套东西。它之所以成为入门标配,是因为它把JavaWeb里所有核心环节全部串起来了:你写一个Servlet,用户输入用户名密码,Servlet去调Service,Service调Dao,Dao用JDBC查MySQL,查完结果存到request域里转发给JSP渲染,浏览器拿到HTML。这一条完整链路跑通,JavaWeb的地基就算打好了。

我在设计这个项目时,特意没急着上Spring Boot,而是用最原始的Servlet + JSP。原因很简单:如果你在还没见过“请求是怎么进到Java代码”的时候就直接用Spring Boot,你会觉得Controller加个注解就能接收请求是理所应当的,结果遇到一次404或中文乱码就完全不知道从哪排查。相反,把Servlet这条链路亲手走一遍,你才会真正理解HTTP请求、请求域、转发和重定向到底是怎么回事。后续你学Spring MVC,会发现它就是把Servlet封装了一层,思路完全一致。

这里也顺便解释一下为什么很多人搜“JavaWeb项目完整案例”会看到MySQL和JSP这两个关键词焊在一起。MySQL是免费且足够教学用的关系型数据库,JSP是当年JavaWeb页面渲染的主力技术,就算现在主流开发已经用前后端分离加模板引擎了,但老教程、学校的课设、以及很多培训机构的基础阶段,依然以JSP作为“服务端怎么把数据变成页面”的入门切入口。所以“JavaWeb_01”用这套组合不仅是历史惯性,也是教学梯度上的合理选择。

1.2 技术选型:为什么是Servlet + JSP,而不是Spring Boot

直接说结论:如果你已经在工作项目里写过Spring Boot,那今天这个话题对你来说可能偏基础;但如果你是自学JavaWeb,我强烈建议先花几个晚上把原生Servlet写一遍。下面这个对比表是我自己后来回头看才整理清楚的:

对比维度Servlet + JSP 原生项目Spring Boot
请求入口自己写Servlet,配置web.xml或注解映射DispatcherServlet统一入口,Controller注解
数据层自己写JDBC,手动处理连接和关闭Spring Data JPA / MyBatis,无需手动管理连接
页面渲染JSP + JSTL手动迭代列表Thymeleaf模板或前后端分离+JSON
Web服务器自己下载Tomcat并配置到IDEA内嵌Tomcat,直接启动main方法
学习成本高,但底层原理曝光低,但容器细节被隐藏

我并不是说非要用老技术,而是说“先难后易”的路线对理解后端很有帮助。你用Spring Boot时,无非是把Servlet流程压缩成了一个@RequestMapping,但Controller为什么能拿到请求参数,ModelAndView里的数据是怎么跑到页面上的,这些概念你只有亲手写过一个Servlet才真正有体感。等到你吃过JSP的苦,反过来再用Spring Boot,你会觉得这工具太体贴了,而不至于一出问题就迷茫。

1.3 目录分层的原则:为什么Service和Dao不能乱写

我在一开始敲项目时,习惯把所有代码塞到Servlet里,查完数据库直接拼HTML返回,几百行一个类,当时觉得挺爽。后来项目功能一多就崩了:改一个页面样式要动Java代码,改一个SQL要翻半天Servlet,根本没有复用的空间。所以JavaWeb_01的工程结构必须从一开始就走分层,哪怕项目再小也分。

经典的分法是Controller(Servlet)负责接收请求和转发页面,Service负责业务逻辑,Dao负责数据库读写,再加上一个Entity包放实体类。简单来说,Controller像公司的前台,只负责接电话和转接,不负责真正干活;Service像业务部门,知道业务流程怎么走;Dao像仓库管理员,只管从仓库取货放货,不关心货被拿去干嘛。各管一段,出问题的时候你才能顺着调用链快速定位是哪个环节出错。

2. 开发环境与工程搭建

2.1 JDK、Tomcat、Maven、IDEA的版本搭配

JavaWeb_01这个项目在环境配置阶段,可能是劝退率最高的环节。我见过太多人卡在“IDEA启动Tomcat后浏览器404”或者“Tomcat版本和JDK版本不兼容”上面。先说版本搭配,我实际测试比较稳的组合是这样:

  • JDK 8 或 11(如果你机器上装的是更高的JDK,比如17或21,建议第一个项目还是用JDK 8跑,兼容性最好)
  • Tomcat 8.5 或 Tomcat 9(这两个版本在Servlet规范上是4.0以下,支持传统Servlet开发和JSP,行为最标准)
  • Maven 3.6 到 3.8 都行,做这种小项目不挑版本
  • IDEA 2020 之后任意版本,社区版也能跑,只不过需要手动配置Tomcat插件或者外部容器的启动方式

这里必须重点说明一下:Tomcat 10以上的版本,Servlet类所在的包名从javax.servlet改成了jakarta.servlet,如果你用的是Tomcat 10而代码里还用javax.servlet,编译能过但运行时直接ClassNotFoundException。所以初学阶段别装最新版Tomcat,否则你会在一个跟业务完全无关的包名问题上面浪费很多时间。我当初就是因为下了Tomcat 10,代码怎么改都报错,最后换成Tomcat 8.5才顺畅了。

2.2 在IDEA中配置Tomcat并创建第一个Servlet

IDEA里跑JavaWeb项目的配置步骤,说简单也简单,说烦也烦,关键是你得理解IDEA和Tomcat之间是通过什么方式协作的。第一步,用Maven创建一个Web项目骨架(maven-archetype-webapp)。建好之后先别急着写代码,去检查一下src/main/java目录是否存在,不存在就手动新建,然后右键标记为Sources Root。

接下来配置Tomcat。打开Run → Edit Configurations,点左上角加号,选择Tomcat Server → Local。在Application Server那里选择你本地Tomcat的安装目录,IDEA会自动识别版本。然后切到Deployment选项卡,点加号添加Artifact,选择那个以war作为扩展名的项目。这里有个关键细节:Application Context建议填写为/,这样访问时直接用http://localhost:8080/项目路径,不用在URL里再带一层前缀。我曾经填成默认的/JavaWeb_01_war_exploded,每次调试浏览器里一长串路径,看着难受还容易输错。

创建Servlet就简单了,直接新建一个类继承HttpServlet,覆写doGet和doPost方法,然后加上@WebServlet("/login")注解,或者在web.xml里配置映射路径。两种方式都行,我建议用注解,少写一点XML配置。需要注意注解里的路径必须用/开头,不是login,不是login.do,就是/login。这个细节我踩过坑,本来URL是http://localhost:8080/JavaWeb_01/login,如果注解写成"login",启动时会报“Servlet映射路径必须从/开始”的错误。

2.3 Web项目目录结构:标准Maven布局与常见误区

一个标准的Maven Web项目目录结构长这样:

JavaWeb_01 ├── pom.xml ├── src │ └── main │ ├── java │ │ └── com/example/xxx │ │ ├── controller │ │ ├── service │ │ ├── dao │ │ ├── entity │ │ └── util │ └── webapp │ ├── WEB-INF │ │ └── web.xml │ └── jsp

src/main/java放Java源代码,src/main/webapp放JSP页面、静态资源(CSS、JS)和web.xml。有一个新人非常容易犯的错误,是把JSP直接放到src/main/java下面,然后到处找不到页面。另一个容易踩的坑是WEB-INF目录下的页面不能通过浏览器直接访问,你必须通过Servlet转发进入。这个特性其实是个天然的保护屏障,把需要登录后才能看的页面放进去,别人就没有办法绕过Servlet直接打开JSP了。

在包名设计上,我建议统一用反向域名风格,比如com.example.demo,尽量别用拼音单字母包名。因为后面你引入的工具类、第三方库类多了,包名乱七八糟会导致import冲突,排查起来很痛苦。

3. 核心功能实现与关键代码

3.1 数据库设计与初始化:从建表SQL到连接参数

“JavaWeb_01”这种项目不需要复杂的表结构,我设计了一张用户表,核心字段包括用户ID、用户名、密码、邮箱、创建时间。密码字段这里多说一句,实际项目中绝对不能用明文保存,至少要加个MD5或BCrypt加密。但教学项目里经常就偷懒直接存字符串了,我建议你在自己做练习时就养成习惯,哪怕只是MD5加盐也好,等你以后做正式项目就会感谢当年这个习惯。

初始化SQL演示一下:

CREATE DATABASE IF NOT EXISTS javaweb_01 DEFAULT CHARACTER SET utf8mb4; USE javaweb_01; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', password VARCHAR(64) NOT NULL COMMENT '密码', email VARCHAR(100) DEFAULT NULL COMMENT '邮箱', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; INSERT INTO t_user (username, password, email) VALUES ('admin', 'admin123', 'admin@example.com'), ('zhangsan', '123456', 'zhangsan@example.com');

这里有一个容易被忽略的关键点:建库时一定要指定utf8mb4字符集,utf8其实在MySQL里是utf8mb3,它不支持存储Emoji和某些生僻汉字。之前我在一个项目里把中文用户名存进数据库,结果控制台能看到中文,前端显示也没问题,但用SQL查询时字段值变成了一堆问号,排查了半天最后发现是表字符集用了utf8而不是utf8mb4,而且连接URL里没有加characterEncoding=utf8,才导致连接层面和存储层面双重乱码。

3.2 JDBC连接:驱动版本和连接池的取舍

我看了很多JavaWeb完整案例,数据访问层清一色用的是JDBC。这个技术说实话现在生产环境已经很少直接用裸JDBC了,但你学习阶段必须写一遍,不然你理解不了MyBatis到底帮你省了哪些事情。

JDBC连接数据库的第一步是加载驱动。MySQL 8.0以上的版本,驱动类名是com.mysql.cj.jdbc.Driver,而MySQL 5.x是com.mysql.jdbc.Driver。如果你用的是MySQL 8.x,还必须在连接URL后面追加时区参数,例如serverTimezone=Asia/Shanghai和useSSL=false,否则驱动初始化的时候会报时区无法识别。

我实际项目里是搞了一个JdbcUtil工具类,静态代码块里注册驱动,然后对外提供获取连接和关闭连接的方法。这样Dao层写代码不用每次重复Class.forName,简单清爽。初学者最容易犯的错误是每次都手动DriverManager.getConnection,用完也不关,导致数据库连接数量上升到最后报Too many connections。哪怕是个练习项目,也请你在getConnection和close上保持好习惯。

3.3 实体类与BaseDao封装:少写重复代码的关键

实体类的写法非常机械,字段对应数据库表的列,配上getter/setter。我建议属性类型用包装类型而不是基本类型,比如用Integer而不是int。为什么?因为数据库字段可以为NULL,基本类型int没法表达“没有值”这个状态,封装类型就可以。虽然这个项目里ID设置了自增,理论上不会为NULL,但养成这个习惯能让你以后应对复杂的表结构时少很多麻烦。

Dao层是重头戏。我刚开始写时,每个方法都来一套“获取连接、创建PreparedStatement、执行SQL、处理结果集、关闭资源”的流程,写三个方法就烦了。所以可以把公共部分抽出来,先写一个BaseDao,用泛型加反射,把查询结果自动封装成实体对象。对于新手,我这里不太建议立刻去搞那种全自动ORM工具,而是先写一个简单的queryForList方法,思路无非就是把ResultSet里的数据通过反射塞进对象的对应属性。

当然,还有一点千万注意:SQL语句里的参数不要用字符串拼接,一定要用PreparedStatement的占位符。比如SELECT * FROM t_user WHERE username = ?,然后setString(1, username)。用拼接的话,用户输入一个' OR '1'='1就能直接把你整个用户表都查出来,这就是最经典的SQL注入。黑马教程里也是反复强调这一点,我在这里再帮大家加深一遍印象。

3.4 Service层的业务逻辑:登录判断和查询分页

Service层是很多人写着写着就忽略的层,觉得Servlet里直接调Dao不就行了,干吗多此一举。但你以后做复杂项目就知道了,一次请求可能既要查用户表,又要查订单表,还要判断库存,这些逻辑如果都堆在Servlet里,过几个星期你自己都看不懂。

这里以登录功能为例。LoginService里定义一个login(String username, String password)方法,返回User对象。逻辑很简单:先根据用户名查用户,如果查不到返回null;如果查到了,比较密码,密码一致就返回这个User,不一致就返回null。不要问为什么登录判断不直接写在Servlet里,因为登录可能不止在网页端用,以后你可能会加手机端接口,Service层复用才是合理的。

再聊分页查询。新手写列表页面,第一版往往是把表里所有数据查出来放到页面里,但数据一多页面就卡,所以必须分页。分页SQL的核心是LIMIT ? , ?,第一个问号是偏移量,第二个问号是每页条数。偏移量计算公式是(当前页码 - 1) * 每页条数。这个公式你要理解而不是硬背,因为它是所有前端分页后端的公共逻辑,以后用MyBatis Plus的Page对象时,底层也是这个思路。

Service层还需要处理一个细节:参数校验。比如每页条数不能为0,不能为负数,当前页码不能超过总页数。你可以把这些校验写在Service里,也可以写个公共工具类。项目本身很小,不要求你上什么重型校验框架,但基础的防御性编程意识要有。

3.5 Controller层:doGet和doPost的处理模式

Servlet里最核心的就是doGet和doPost方法。我建议约定:展示页面的请求用doGet,提交表单修改数据的请求用doPost。这是HTTP语义的良好实践,也能让代码逻辑更清晰。

登录Servlet大概长这样:

@WebServlet("/login") public class LoginServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/jsp/login.jsp").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); UserService userService = new UserService(); User user = userService.login(username, password); if (user == null) { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("/jsp/login.jsp").forward(request, response); } else { request.getSession().setAttribute("loginUser", user); response.sendRedirect(request.getContextPath() + "/user/list"); } } }

注意上面这段代码的细节:登录成功后我用的是sendRedirect而不是forward。为什么?因为如果用了forward,URL地址栏停留在/login,用户按一次刷新表单会重复提交,登录逻辑会被执行两次。而重定向先发一个302让浏览器访问/user/list,刷新时刷新的是列表页,不会重复提交登录。这个细节是教科书里很少讲,但实际开发特别重要的东西。

另外,我建议Servlet里不要直接new Service对象并自己管理生命周期。项目小倒无所谓,但你应该知道这种写法在大型项目里会带来对象创建频繁和事务管理混乱的问题。更好的方式是后面学Spring再用IOC容器接管,现在这个阶段理解“Servlet → Service → Dao”的调用方向比优化对象管理更重要。

3.6 JSP页面与JSTL:用EL表达式替代Java代码

JSP页面写起来是个体力活,但里面也藏着很多门道。我最初写JSP时,喜欢直接在<%%>里写Java代码循环,后来很快就体验到了什么叫“代码和页面混在一起想死的心都有”。改进方式就是用JSTL标签库加EL表达式。

列表页面的核心片段大概是这样的:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> ... <c:forEach items="${requestScope.page.list}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.email}</td> <td>${user.createTime}</td> </tr> </c:forEach>

这里很容易踩一个坑,就是JSTL没有引入导致c标签不能被解析,页面直接报500或者显示${user.username}原样文本。排查方式也很简单:先确认pom里有没有引入javax.servlet.jsp.jstl和taglibs-standard-impl这两个依赖,再确认JSP头部有没有写taglib指令。两者缺了任何一个都不行。

再说说URL编码问题。点击删除或者修改的时候,通常会在链接上带一个用户ID,比如/user/delete?id=3。如果这个ID是从查询参数里获取的,在Servlet里要记得做空判断,不然用户手动访问一个没有id的删除链接,Integer.parseInt(request.getParameter("id"))会抛NumberFormatException。我的习惯是先取到字符串,判断非空且是数字,再parseInt,别图省事。

3.7 页面之间的跳转方式:转发与重定向的边界

转发和重定向的区别,学的时候觉得简单,用的时候容易混。转发request.getRequestDispatcher("/jsp/list.jsp").forward(...),整个过程是一次请求,浏览器URL不变,request域里的数据能带过去。重定向response.sendRedirect("路径")是两次请求,URL会改变为新的路径,request域里setAttribute的数据全部丢失。

什么时候用哪种,我的经验口诀是:要带数据给页面显示,用转发;要防止表单重复提交、或者登录成功之后跳到主页,用重定向。比如查询用户列表,必须在Servlet里查到List,存到request域,再转发给JSP渲染,所以这里用转发。比如登录成功后,用户信息和登录状态都存在Session里了,不需要再经过一次request域传值,那么用重定向更干净,同时也能把URL从/login变成/user/list,用户一目了然自己到了哪个页面。

4. 常见问题与排查技巧实录

4.1 “404找不到Servlet”时的四个排查方向

这类项目里404是家常便饭。第一次遇到别慌,按照下面四个方向一层层查基本都能解决。

第一个方向是URL路径。浏览器访问的地址是不是和@WebServlet注解里的值完全一致?注意大小写和拼写。第二个方向是项目部署上下文。如果IDEA的Deployment里配置了Application Context是/JavaWeb_01_war_exploded,那你访问时也必须带上这个前缀。第三个方向是Tomcat没有把项目部署上去。手动打开Tomcat的webapps目录看看有没有生成对应的目录,这种方式在IDEA集成部署环境里不太常见,但外部部署时可作为参考。第四个方向要看IDEA的Artifact配置是否正确,确保项目已添加了artifacts并放到Tomcat容器的Deployment里。

我印象最深的一次,是在把项目从别人电脑拷贝到我电脑后,IDEA重新关联Tomcat时出了点问题,之前配好的项目代码部署路径变了,启动后直接404。后来我把IDEA中的Artifact删掉重新添加,恢复到了项目名,路径就通了。总结下来,IDEA里跑Web项目出404,八成不是Java代码的问题,而是工程配置层面的问题。

4.2 中文乱码:一个项目里会出现四层不同的地方

中文乱码是JavaWeb新手最容易崩溃的问题,因为它可能出现在浏览器页面、IDEA控制台、数据库表、HTTP响应这四个不同的位置,表现还各不相同。我做这个项目时,至少遇到了三次乱码,每一层的原因还不一样。

第一层是浏览器显示乱码。解决办法是JSP页面顶部加<%@ page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" %>,并且保证文件本身保存格式是UTF-8。IDEA右下角能看到当前文件编码,如果不是UTF-8就要手动转换。

第二层是请求参数乱码。Tomcat 8.0以上版本的POST请求默认已经按UTF-8解码,但GET请求还是会有问题。最保险的做法是在Servlet里对请求设置编码:request.setCharacterEncoding("UTF-8")。另外如果使用Spring框架,会有现成的CharacterEncodingFilter去处理,但Servlet阶段就要手写。

第三层是响应乱码。在写response.getWriter()之前执行response.setContentType("text/html;charset=UTF-8"),否则页面显示的中文可能全是问号。第四层是数据库乱码。除了建库建表用utf8mb4之外,MySQL连接URL里也要带characterEncoding=utf8,整条链路任何一个环节编码不统一,数据到了库里就毁掉了。

我建议你按“页面 → 请求 → 响应 → 数据库”这条链路去梳理,哪里出现问号就先看哪一层,别一看到乱码就直接怀疑数据库,很多情况下根本到不了数据库那一步。

4.3 Tomcat启动失败:端口占用和启动日志的读法

Tomcat启动失败最常见的原因就是8080端口被占用。Windows上在cmd里执行netstat -ano | findstr 8080,Linux/Mac上用lsof -i:8080,找到占用进程之后,如果确实是某些程序占用了,可以换一个端口,也可以在IDEA的Tomcat配置页把HTTP port改成9090或8081。这里有个测试技巧:改端口后访问地址也要一起改,不然你在浏览器里输的还是8080,自然会觉得Tomcat没起来。

另一个看一眼就能判断的问题:Tomcat启动时的日志里如果出现“LifecycleException”或者“Unable to open debugger port”,那多半是IDEA的Debug端口被占用了,不是Tomcat本身的问题。把断点监听相关端口关掉,或者重启IDEA就能解决。如果你实在看不懂日志,直接用浏览器访问http://localhost:8080,如果能看到Tomcat默认首页,说明Tomcat本身正常,问题出在你的项目部署上。

4.4 JDBC连接失败的常见症状和应对

数据库连接失败的理由也很多。一种是在MySQL 8.0中使用了旧驱动类com.mysql.jdbc.Driver,控制台会直接提示驱动类不存在。解决方式很直接:改成com.mysql.cj.jdbc.Driver。另一种是连接URL里没写serverTimezone,启动时可能报“Server returns invalid timezone”。加上serverTimezone=Asia/Shanghai就可以。

还有一个小坑是MySQL密码和root权限的问题。你的Java项目里配置的数据库密码必须和本地MySQL实际密码一致,拼写不要错。如果连着都失败,可以先在命令行工具里确认一下能否用同样账号密码登录MySQL,如果命令行能连但Java代码连不上,再考虑是不是驱动或URL的问题。

4.5 JSP里取不到request属性值的问题

JSP里取不到request里的值也是个高频问题。通常有三个原因。第一个是转发和重定向搞混了,重定向后request域是新的,之前Servlet里setAttribute的值自然就消失了。第二个是页面里写错了属性名,Servlet里set了page,JSP里用了${list},肯定取不到,注意一致性。第三个是EL表达式默认作用域查找顺序的问题,${username}会依次从page、request、session、application四个域里查找,如果session里恰好有个同名属性,可能会覆盖request里的值。为了避免混淆,我建议显式写${requestScope.username},虽然长了点,但可读性和可维护性大幅提升。

4.6 常见问题速查表

症状可能原因处理方向
访问所有页面都404Application Context路径不对查看IDEA Deployment配置
Servlet报ClassNotFoundException(javax.servlet)Tomcat版本太高(≥10)换Tomcat 9或改用jakarta包
页面中文全是问号JSP编码或response编码不是UTF-8页面指令加charset=UTF-8
数据库存中文乱码表字符集不是utf8mb4建表时指定utf8mb4
SQL出现’or’之类数据泄漏SQL拼接注入改用PreparedStatement占位符
表单重复提交用转发导致URL未变化成功后改用重定向
EL表达式显示为原样文本JSTL依赖或标签库未引入检查pom和taglib指令

5. 我踩过坑之后,如果再让我做一次这个项目,我会怎么改

5.1 用Listener或Filter解决编码初始化,而不是在每个Servlet里设置

做JavaWeb_01这个项目的时候,我几乎在每个Servlet里都写了request.setCharacterEncoding("UTF-8"),写得很烦,而且漏写就乱码。后来学了Filter,才发现完全可以用一个CharacterEncodingFilter统一搞定。你再回头看Spring MVC的源码,其实就是内置了类似功能的过滤器。所以项目跑到后面如果觉得重复代码太多,是时候学一下Filter和Listener了,它们就是Servlet体系里的“公共拦截逻辑”和“生命周期钩子”。

5.2 把连接池换成Druid,提前了解生产环境的数据库连接管理

原生JDBC每次获取连接都是通过DriverManager创建新连接,请求量一大性能就会很差。聪明一点的方案是用连接池,启动时创建一批连接放在池子里,用的时候借,用完还回去。Alibaba的Druid连接池是JavaWeb时代使用率很高的选择,而且它自带监控页面,你能看SQL执行时间、活跃连接数。虽然JavaWeb_01这种小项目不需要监控,但把Druid用在项目里,比裸JDBC更贴近实际开发场景。

5.3 拆分BaseServlet,用反射省掉一堆重复Servlet

原项目中登录、注册、列表、删除、跳转编辑页,每个功能我都写了一个Servlet,最后项目里十几个Servlet类,每个都很短但数量很多。后来学到的一个优化方案是写一个BaseServlet,里面通过反射根据请求参数(比如method=list)调用子类的具体方法。这样很多操作就能合并到一个Servlet里。你以后看很多开源项目里的@WebServlet写法以及Spring MVC的DispatcherServlet,底层都有类似的“分发器”设计思想。

5.4 把目标定得更远:从Servlet项目平滑过渡到Spring Boot

等你把JavaWeb_01这个项目完整跑通之后,我建议你用同样的数据库和页面,再照着Spring Boot + MyBatis写一遍同一个用户管理。你会发现控制器从继承HttpServlet变成了@Controller,Dao从JdbcUtil变成@Mapper接口,页面渲染从JSP换成模板引擎,整个开发体验完全不一样。但正因为你做过一遍Servlet版本,你才能看懂Spring Boot究竟在哪里做了封装、哪里做了简化、以及它为什么推荐用前后端分离。到了这一步,你才算真正完成了Java后端的学习闭环。

我个人在实际操作中的体会是:一个项目反反复复做三遍,比走马观花看十个教程有用。第一遍照着视频敲,第二遍全删了盲写,第三遍想办法优化代码结构。当你能把这个JavaWeb_01拆成一段清晰的MVC链路讲给别人听,并且遇到404不慌、乱码不乱改的时候,你就可以安心往下走SpringBoot和更多的中间件了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 18:12:02

纯CSS3实现发光渐变Loading动画:原理拆解与性能优化实践

简介&#xff1a;纯CSS3网页加载动画源码包&#xff0c;面向前端初学者与网页设计者&#xff0c;为页面加载环节增添科技感视觉反馈&#xff0c;解决等待页枯燥乏味、缺乏动态引导的问题&#xff0c;全程不依赖JavaScript或其他库。压缩包共2个文件&#xff1a;1个HTML页面负责…

作者头像 李华
网站建设 2026/9/26 18:11:13

用Claude Code与ffmpeg打造代码化视频生产流水线

1. 从"video-use"这个模糊标题说起&#xff1a;它到底想解决什么问题第一次看到"video-use"这个标题&#xff0c;加上空白的正文和关键词&#xff0c;我脑子里第一反应是&#xff1a;这大概率是一个围绕"用代码驱动视频生产"的工具集或者工作流项…

作者头像 李华
网站建设 2026/9/26 18:11:11

六天烧两千万:大规模强化学习训练开源MoE模型全拆解

1. 从一条热搜说起&#xff1a;为什么这次开源模型的动作值得关注前几天刷到一条消息&#xff0c;说某团队在六天时间里烧掉了两千多万算力成本&#xff0c;就为了训练一个开源模型&#xff0c;而且多个 Agent 基准测试的成绩直接对标闭源旗舰。这个数字乍一看挺吓人&#xff0…

作者头像 李华
网站建设 2026/9/26 18:11:11

小米开源模型MiMo-V2.6:大规模强化学习如何炼成比肩闭源旗舰的Agent能力

1. 从标题拆解这次开源模型的技术路线1.1 标题里藏着的三个关键信号看到“罗福莉押注大规模RL、小米最强开源模型亮相”这个标题&#xff0c;我第一反应不是去看参数表&#xff0c;而是去拆标题里的动词和名词。“押注”这个词很重&#xff0c;说明这不是一次常规的版本迭代&am…

作者头像 李华