我当年第一次跑通“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去调ServiceService调DaoDao用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模板或前后端分离JSONWeb服务器自己下载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的工程结构必须从一开始就走分层哪怕项目再小也分。经典的分法是ControllerServlet负责接收请求和转发页面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并创建第一个ServletIDEA里跑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 │ └── jspsrc/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 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; INSERT INTO t_user (username, password, email) VALUES (admin, admin123, adminexample.com), (zhangsan, 123456, zhangsanexample.com);这里有一个容易被忽略的关键点建库时一定要指定utf8mb4字符集utf8其实在MySQL里是utf8mb3它不支持存储Emoji和某些生僻汉字。之前我在一个项目里把中文用户名存进数据库结果控制台能看到中文前端显示也没问题但用SQL查询时字段值变成了一堆问号排查了半天最后发现是表字符集用了utf8而不是utf8mb4而且连接URL里没有加characterEncodingutf8才导致连接层面和存储层面双重乱码。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后面追加时区参数例如serverTimezoneAsia/Shanghai和useSSLfalse否则驱动初始化的时候会报时区无法识别。我实际项目里是搞了一个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 11就能直接把你整个用户表都查出来这就是最经典的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。为什么因为如果用了forwardURL地址栏停留在/login用户按一次刷新表单会重复提交登录逻辑会被执行两次。而重定向先发一个302让浏览器访问/user/list刷新时刷新的是列表页不会重复提交登录。这个细节是教科书里很少讲但实际开发特别重要的东西。另外我建议Servlet里不要直接new Service对象并自己管理生命周期。项目小倒无所谓但你应该知道这种写法在大型项目里会带来对象创建频繁和事务管理混乱的问题。更好的方式是后面学Spring再用IOC容器接管现在这个阶段理解“Servlet → Service → Dao”的调用方向比优化对象管理更重要。3.6 JSP页面与JSTL用EL表达式替代Java代码JSP页面写起来是个体力活但里面也藏着很多门道。我最初写JSP时喜欢直接在%%里写Java代码循环后来很快就体验到了什么叫“代码和页面混在一起想死的心都有”。改进方式就是用JSTL标签库加EL表达式。列表页面的核心片段大概是这样的% taglib urihttp://java.sun.com/jsp/jstl/core prefixc % ... c:forEach items${requestScope.page.list} varuser 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?id3。如果这个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 pageEncodingUTF-8 contentTypetext/html;charsetUTF-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;charsetUTF-8)否则页面显示的中文可能全是问号。第四层是数据库乱码。除了建库建表用utf8mb4之外MySQL连接URL里也要带characterEncodingutf8整条链路任何一个环节编码不统一数据到了库里就毁掉了。我建议你按“页面 → 请求 → 响应 → 数据库”这条链路去梳理哪里出现问号就先看哪一层别一看到乱码就直接怀疑数据库很多情况下根本到不了数据库那一步。4.3 Tomcat启动失败端口占用和启动日志的读法Tomcat启动失败最常见的原因就是8080端口被占用。Windows上在cmd里执行netstat -ano | findstr 8080Linux/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”。加上serverTimezoneAsia/Shanghai就可以。还有一个小坑是MySQL密码和root权限的问题。你的Java项目里配置的数据库密码必须和本地MySQL实际密码一致拼写不要错。如果连着都失败可以先在命令行工具里确认一下能否用同样账号密码登录MySQL如果命令行能连但Java代码连不上再考虑是不是驱动或URL的问题。4.5 JSP里取不到request属性值的问题JSP里取不到request里的值也是个高频问题。通常有三个原因。第一个是转发和重定向搞混了重定向后request域是新的之前Servlet里setAttribute的值自然就消失了。第二个是页面里写错了属性名Servlet里set了pageJSP里用了${list}肯定取不到注意一致性。第三个是EL表达式默认作用域查找顺序的问题${username}会依次从page、request、session、application四个域里查找如果session里恰好有个同名属性可能会覆盖request里的值。为了避免混淆我建议显式写${requestScope.username}虽然长了点但可读性和可维护性大幅提升。4.6 常见问题速查表症状可能原因处理方向访问所有页面都404Application Context路径不对查看IDEA Deployment配置Servlet报ClassNotFoundExceptionjavax.servletTomcat版本太高≥10换Tomcat 9或改用jakarta包页面中文全是问号JSP编码或response编码不是UTF-8页面指令加charsetUTF-8数据库存中文乱码表字符集不是utf8mb4建表时指定utf8mb4SQL出现’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里面通过反射根据请求参数比如methodlist调用子类的具体方法。这样很多操作就能合并到一个Servlet里。你以后看很多开源项目里的WebServlet写法以及Spring MVC的DispatcherServlet底层都有类似的“分发器”设计思想。5.4 把目标定得更远从Servlet项目平滑过渡到Spring Boot等你把JavaWeb_01这个项目完整跑通之后我建议你用同样的数据库和页面再照着Spring Boot MyBatis写一遍同一个用户管理。你会发现控制器从继承HttpServlet变成了ControllerDao从JdbcUtil变成Mapper接口页面渲染从JSP换成模板引擎整个开发体验完全不一样。但正因为你做过一遍Servlet版本你才能看懂Spring Boot究竟在哪里做了封装、哪里做了简化、以及它为什么推荐用前后端分离。到了这一步你才算真正完成了Java后端的学习闭环。我个人在实际操作中的体会是一个项目反反复复做三遍比走马观花看十个教程有用。第一遍照着视频敲第二遍全删了盲写第三遍想办法优化代码结构。当你能把这个JavaWeb_01拆成一段清晰的MVC链路讲给别人听并且遇到404不慌、乱码不乱改的时候你就可以安心往下走SpringBoot和更多的中间件了。
企业数字化 ERP 产品动态
相关推荐
Claude Code 接入 GLM-5.1 报错排查:Base URL 与 API Key 通道配置指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 18:13:34
国开学位英语高频固定搭配500条实战指南 1. 这份“国开学位英语高频固定搭配500条”到底是什么,谁该用、怎么用才不白背?“国开学位英语 高频固定搭配 500 条(背诵版)核心 300 条 进阶 200 条,覆盖考试搭配考点 90%”——这个标题不是营销话术,而… · 2026/9/26 18:13:34
中兴RNC系统结构详解:接口、硬件、软件与排障实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 18:13:28
Atlas 300V 24G推理加速卡解读与YOLOv5部署实战指南 最近被问得最多的一个问题:Atlas 300V 24G 到底是不是运算加速卡,能不能直接拿来部署 YOLO?这问题看似一句话能回答,但真要把它讲清楚,得从硬件定位、软件工具链一直聊到模型转换和推理优化。这篇文章我打算把事情一次… · 2026/9/26 19:16:19
桌面端登录报错排查指南:从网络、配置到运行环境的完整链路 1. 桌面端登录报错到底卡在哪一环桌面版客户端登录报错,是很多人装完软件后遇到的第一个拦路虎。界面通常表现为转圈、白屏、提示"无法加载登录要求"或者干脆闪退,重装一遍还是老样子。这类问题的本质,绝大多数时候不是软件本身坏了… · 2026/9/26 19:16:19
装配制造行业 MES 系统物料齐套技术应用详解 1. 引言在装配制造行业,生产节拍高度依赖物料供应的准确性与及时性。与流程型制造不同,装配型产品通常由大量零部件按一定顺序组装而成,任何一个关键物料的缺失都可能导致整条产线停线、换型或临时插单,造成产能损失和交付延误。因… · 2026/9/26 19:16:13
AI-Agent时代-SQL与向量数据库-学习笔记-CSDN版 AI Agent 时代,SQL 会被 AI 取代吗?——SQLAI 结合路径的学习笔记 这是一篇学习笔记,记录我对「AI Agent 时代数据库查询语言未来」的完整思考: ① SQL 如何与 AI 结合并落地应用;② SQL 会不会被自然语言彻底取代、会不会出现新查询语言; ③ SQL 与向量数据库之间到底是… · 2026/9/26 19:16:13
Hello-Agents 第9章:多轮对话上下文工程与消息累积实战 1. 从一次终端里的翻车说起:为什么第二轮对话丢了 Context第一次跑 Hello-Agents 第 9 章的时候,我在终端里盯着日志看了足足十分钟,脑子里只有一个念头:这不对。第一轮请求明明把系统提示、用户输入、工具描述全都塞进去了&#… · 2026/9/26 19:16:13
研究生数学建模竞赛实战作战系统:问题解构×流程管控×团队协同 1. 这不是“刷题指南”,而是一套可复用的建模作战系统“华为杯”研究生数学建模竞赛,每年九月底开赛,四天三夜,72小时极限交付——它从来不是比谁算得快、谁模型新,而是比谁能在信息混沌中快速锚定问题本质,… · 2026/9/26 19:16:00
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46