J2EE编程技术源代码部署实战:从Tomcat运行到Spring Boot迁移 简介郝玉龙编著的《J2EE编程技术源代码》是一份聚焦Java企业级开发的系统源码合集面向初学J2EE或希望深入理解Servlet、JSP、EJB、JMS、JTA等核心技术的开发者。内容覆盖企业应用常见模块源码层次清晰便于对照学习分布式多层架构的设计与实现。整包为RAR压缩格式共429个文件、12.3MB包含Java源文件91个、JSP页面83个、XML配置97个、编译后Class文件92个另含properties属性配置、TLD标签库描述等辅助文件可支撑动态页面、业务组件、消息服务与事务管理等场景的阅读与实践。目前已有186人学习下载适合作为课程配套资料或项目开发参考。通过这套源码读者可直观看到Servlet与JSP如何协作生成动态网页EJB如何封装可复用业务逻辑JMS与JTA如何支撑异步通信和分布式事务结合不同模块的实例有助于快速建立企业级应用的完整认知并将相关技术整合到实际项目中。1. 拿到J2EE编程技术源代码包先想清楚你想从它身上得到什么如果你买过一本 J2EE 教材或者从课程配套资源里下载过一套 J2EE 编程技术源代码大概率经历过同一个场景源码导入 Eclipse 后满屏红叉Tomcat 一启动就报 ClassNotFoundException折腾一晚上还没看到登录页面长什么样。这套经典教材配套源代码涵盖的正是 JSP、Servlet、JavaBean 和 EJB 这几个 J2EE 核心组件结构上属于典型的早期分层教学项目。它解决的是两件事第一让你直观看到 J2EE 项目在容器里怎么组织代码第二让你在本地把整个请求链路跑通而不是停留在“看书以为自己会了”的阶段。适合三类人刚开始学 J2EE 的学生、刚接手老旧 Java Web 项目的开发者、准备把老代码迁移到 Spring Boot 但又怕漏业务的人。读完这篇你不仅能让它跑起来还能知道每一层代码为什么那样写、参数怎么调、哪些坑值得绕开。2. 把源码包在本地跑起来JDK 版本、中间件选择与最小启动命令老 J2EE 项目和今天的主流 Java Web 工程有个明显差异它依赖 Java EE 规范里 Servlet、JSP、EJB 这些组件而规范的版本与容器的版本是一一对应的。很多人拿到代码后直接装最新版 JDK再去官网下最新的 Tomcat结果启动日志里刷出一堆和 javax 有关的报错。这不是源码坏了是版本对不上。所以把源码跑起来之前先把两个版本定死JDK 版本和容器版本。2.1 先确认 JDK 与容器版本J2EE 1.4 不是随便装个 Tomcat 10 就能跑这套源码对应的技术栈基本处在 J2EE 1.4 到 Java EE 5 之间典型特征是 javax.servlet、javax.ejb 这些包名以及 web.xml 里带http://java.sun.com/xml/ns/j2ee的命名空间。这类项目在 Tomcat 5.5 到 Tomcat 8.5 上都能正常部署但到了 Tomcat 10Servlet API 的包名从 javax 改成了 jakarta老代码里的 import 语句全部失效除非做包名替换否则没法直接运行。我一般会先看两个地方判断项目属于哪个版本一是web.xml头部声明里的 schema 地址和版本号二是lib目录下的 servlet-api.jar 是哪一代。常见的对应关系可以参考下面这张表J2EE / Java EE 版本Servlet API适配的 Tomcat 版本JDK 建议J2EE 1.3 / 1.4Servlet 2.3 / 2.4Tomcat 5.5 / 6.0JDK 1.4 / 5Java EE 5Servlet 2.5Tomcat 6.0 / 7.0JDK 5 / 6Java EE 6Servlet 3.0 / 3.1Tomcat 7.0 / 8.5JDK 6 / 7 / 8实际操作中最省事的组合是 JDK 8 配 Tomcat 8.5。JDK 8 既能编译老代码的语法又不会像更新版本那样把 javax.xml.bind 这类包直接移除Tomcat 8.5 向下兼容 Servlet 2.4 的 web.xml能省掉大量改命名空间的时间。先跑一个命令确认当前 JDK 情况java -version javac -version如果输出里版本号高于 8建议先找一个 JDK 8 装好再设置JAVA_HOMEexport JAVA_HOME/path/to/jdk1.8.0_202 export PATH$JAVA_HOME/bin:$PATH参数说明JAVA_HOME指向 JDK 安装目录而不是 JRE 目录Tomcat 启动脚本依赖这个变量来定位编译器和运行时。PATH里把 JDK 的 bin 放在前面避免系统里其他版本的 java 抢先被调用。这一步做完用java -version再确认一次输出里出现的是 1.8再继续往下走。2.2 用 Tomcat 8.5 把 WAR 包跑起来最小启动命令源码包通常有两种形态一种是直接给你整个 Eclipse 工程目录目录里带.project和.classpath另一种是打包好的 WAR 文件直接丢进 Tomcat 的 webapps 就能发布。如果是前者常见做法是在 Eclipse 里通过 Export 打成一个 WAR或者用命令手工编译后再复制目录。我一般图省事直接在命令行里用 jar 命令打包cd /path/to/project jar -cvf myapp.war .这条命令会把当前目录下的所有内容打进myapp.warc表示创建新包v打印打包过程f指定输出文件名。打包完成后把 war 复制到 Tomcat 的 webapps 目录cp myapp.war /path/to/tomcat8.5/webapps/ cd /path/to/tomcat8.5/bin ./startup.shTomcat 启动后会自动解压 war 文件生成同名的目录。这时用 curl 验证首页能不能访问curl -I http://localhost:8080/myapp/index.jsp命令行参数说明-I表示只请求响应头如果返回HTTP/1.1 200 OK说明项目已经正常发布。如果返回 404先看 context root。默认情况下 context root 就是 war 包名也就是/myapp访问路径拼错是最常见的启动成功但页面打不开的原因。这里有个容易踩的细节老项目的文件编码很可能是 GBK 或 GB2312打包前最好用file命令看清编码file src/com/example/LoginServlet.java如果显示为 ISO-8859 或 GBK而你的 Tomcat 默认使用 UTF-8 读取JSP 页面会出现乱码。建议先把源码文件统一转成 UTF-8 再打包这一步会在后面章节详细展开。2.3 启动失败的三个高频信号与三分钟定位法Tomcat 启动失败时不要只看控制台最后几行logs/catalina.out才是完整日志。常见的失败信号有三个对应三种完全不同的排查方向我分别说一下排查路径。第一个是UnsupportedClassVersionError报错内容类似unsupported class file version 52.0。这说明 class 文件是用高版本 JDK 编译的而 Tomcat 运行的 JRE 版本更老。处理方式要么把 Tomcat 的启动 JRE 切到高版本 JDK要么把项目编译级别降到较低版本。Eclipse 里右键项目选择 Properties在 Java Compiler 里把 Compiler compliance level 调低重新编译再导出。第二个是端口被占用日志里能看到Port 8080 required by Tomcat v8.5 Server is already in use。先查谁占用了端口lsof -i :8080找到占用进程的 PID确认是残留的 Tomcat 实例还是其他服务然后结束对应进程或者把 Tomcat 的 server.xml 里的 Connector port 改成 8081。改端口时注意代码里如果有写死 8080 的跳转地址也要一起改。第三个是NoClassDefFoundError通常集中出现javax.servlet、org.apache.commons.logging这类包。这类错误表示项目编译时能引用到的 jar运行时不在 WEB-INF/lib 下。老项目经常把依赖 jar 放在外部 lib 目录发布时没有一起打进去检查方式是解压 war 看WEB-INF/lib下有没有对应的 jar没有就把依赖补进去重新打包。jar -tf myapp.war | grep servlet-api-t列出 war 包内容f指定文件grep过滤关键字。如果 servlet-api.jar 在列表里通常不会缺这个类如果不在说明打的是“瘦包”运行时必须依赖 Tomcat 自带的类库。看到这里你应该能理解启动问题绝大多数是版本和路径两件事先把这两点对齐代码基本不用改就能跑起来。3. 看懂分层架构一次登录请求在 Bean、Servlet、JSP 之间怎样流动把源码跑起来只是第一步接下来要回答“这套代码到底在写什么”。J2EE 教学源码的核心价值在于它用最简单的样例展示了一个分层模型JSP 负责展示页面Servlet 负责接收请求和控制流程JavaBean 负责业务数据和逻辑。如果只看单个文件会觉得代码很简单但把整个请求串起来才能理解为什么当年要这么设计。3.1 一次登录请求的完整流转从 login.jsp 到数据库假设这套源码里有一个用户登录功能页面流程是这样的用户在 login.jsp 输入用户名和密码点击提交按钮表单数据交给 LoginServletLoginServlet 调用 UserBean 里的方法去和数据库里的用户表比对最后把结果返回给 welcome.jsp 或错误提示页。对应的完整调用路径可以分成六步浏览器发起 POST 请求地址是http://localhost:8080/myapp/login.doTomcat 根据 web.xml 中的映射配置把请求转给 LoginServlet 的 doPost 方法LoginServlet 调用request.getParameter(username)拿到表单里的用户名LoginServlet 创建 UserBean 对象调用user.checkLogin(username, password)UserBean 内部通过 JDBC 连接数据库执行 SELECT 语句返回 boolean 结果LoginServlet 根据结果选择 forward 到 welcome.jsp或者重定向回 login.jsp这个流程看起来直接但有个关键点JSP 页面里不应该出现数据库操作代码Servlet 里也不应该出现 HTML 标签。每一层只做自己职责范围内的事这就是 J2EE 分层最核心的约束。你可能觉得这没什么但当你看到很多“简化版”代码把 SQL 写在 JSP 里时就知道这种约束对可维护性有多重要。3.2 UserBean 与 LoginServlet 的可复用片段参数与返回值怎么约定典型的 JavaBean 写法是私有属性加 getter/setter再配一个或多个业务方法。这里以一个 UserBean 的登录校验方法为例代码结构是 J2EE 时代最常见的样式public class UserBean { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public String getPassword() { return password; } public void setPassword(String password) { this.password password; } public boolean checkLogin(String user, String pwd) { // 这里通常调用 DBUtil 获取连接执行 SQL 查询 // 实际项目中不要在这里写死 SQL而应从配置读取 String sql SELECT COUNT(*) FROM user WHERE username ? AND password ?; // 使用 PreparedStatement 防止 SQL 注入 return queryCount(sql, user, pwd) 0; } }逻辑说明这个 Bean 的checkLogin方法接收两个字符串参数返回一个 boolean 值。它没有直接暴露数据库连接细节而是通过私有方法queryCount去执行查询。这样做的好处是Servlet 层只需要关心“登录成功了吗”不需要知道数据库表结构。参数设计上用户名和密码作为方法参数传入避免把 request 对象传进 Bean保持业务逻辑与 Web 容器解耦。配套的 LoginServlet 常见写法如下public class LoginServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserBean user new UserBean(); boolean ok user.checkLogin(username, password); if (ok) { request.getSession().setAttribute(loginUser, username); request.getRequestDispatcher(/welcome.jsp).forward(request, response); } else { response.sendRedirect(login.jsp?error1); } } }参数说明setCharacterEncoding(UTF-8)要放在getParameter之前否则 POST 请求里的中文参数会乱码。getRequestDispatcher(...).forward(request, response)是服务器内部跳转浏览器地址栏不变适合带 request 属性到页面展示sendRedirect是浏览器二次请求地址栏会变成login.jsp?error1适合登录失败后的场景。两者最大的区别在于forward 可以传递 request 作用域数据redirect 则拿不到。这个知识点是 J2EE 面试里出现频率很高的点也是调试时最容易困惑的地方。3.3 JSP 与 Servlet 的分工为什么页面里尽量不写业务代码老教材为了讲解方便经常在 JSP 里写 Java 片段也就是% %。但一套结构良好的源码页面里应该只有显示逻辑业务判断都在 Servlet 或 Bean 里完成。一个值得参考的 JSP 片段是这样的% page contentTypetext/html; charsetUTF-8 languagejava % jsp:useBean iduserBean classcom.example.UserBean scopepage / jsp:setProperty nameuserBean propertyusername paramusername / html body h3欢迎您${userBean.username}/h3 p登录成功您可以通过下方链接进入主页。/p a href/myapp/index.do进入主页/a /body /html逻辑说明jsp:useBean负责创建或获取指定作用域里的 Bean 实例scopepage表示在当前页面内有效jsp:setProperty把请求参数自动赋值给 Bean 的同名属性paramusername表示从 request 参数里取值。后面的${userBean.username}是 EL 表达式等价于调用userBean.getUsername()。这种写法的好处是页面里不再有 Java 代码后端改逻辑时不用动 JSP。不过它也带来一个小坑jsp:setProperty自动匹配同名参数如果表单字段名和 JavaBean 属性名不一致会赋不进去。所以约定参数名和属性名保持一致是一个值得养成的习惯。你也看到了J2EE 分层思想本身没什么高深的真正高深的是在写代码时始终守住边界Servlet 不拼 HTMLJSP 不写 SQLJavaBean 不依赖 HttpServletRequest。守住这三点老代码即便结构陈旧也仍然能被安全地拆解和复用。4. 三个必调的配置文件web.xml、数据源与 JSP 编码J2EE 项目和普通 Java 工程最大的区别之一就是大量行为靠配置文件驱动。改动一个 XML 标签可能比改十行 Java 代码影响面还大。而在这套源码里最需要关注的配置文件就三个web.xml决定请求怎么路由数据源配置决定数据库能不能连上JSP 编码决定页面显示的是中文还是乱码。把这三个文件吃透整个项目的可维护性就掌握了大半。4.1 web.xmlServlet 映射、欢迎页与版本声明都要改web.xml是整个 Web 应用的入口配置。一个典型的 J2EE 项目版本头部声明里通常带http://java.sun.com/xml/ns/j2ee中间包含若干servlet和servlet-mapping标签。下面是一段可以直接对照修改的示例?xml version1.0 encodingUTF-8? web-app version2.4 xmlnshttp://java.sun.com/xml/ns/j2ee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd servlet servlet-namelogin/servlet-name servlet-classcom.example.servlet.LoginServlet/servlet-class load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namelogin/servlet-name url-pattern/login.do/url-pattern /servlet-mapping welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list /web-app参数说明load-on-startup的值表示容器启动时加载 Servlet 的顺序数字越小优先级越高写 1 表示应用一启动就实例化这个 Servlet适合需要提前初始化资源的场景。如果不需要可以删掉这一行容器会在第一次请求时再创建实例。url-pattern是请求匹配规则/login.do表示只有访问这个路径才会进入 LoginServlet。老代码里常见的*.do后缀不是规范强制要求的只是一种约定匹配所有以.do结尾的请求。welcome-file-list决定访问根路径时默认打开哪个页面这个配置和访问路径组合起来经常是 404 问题的根源。一个值得注意的点web-app version2.4配的是老式命名空间如果用 Tomcat 8.5 部署这个配置完全兼容。但如果新写项目建议直接用 Jakarta EE 的新命名空间和版本号不要再抄老格式。学习老代码时看懂即可动手的时候还是按新规范来。4.2 数据源配置把连接串从代码里抽出来改密码只动一处老项目的数据库连接最常见的写法是在代码里直接写Class.forName(com.mysql.jdbc.Driver)然后DriverManager.getConnection(url, user, password)。这段代码单独看没有问题但密码散落在多个类里换一次数据库密码要改好几个 Java 文件改漏一个就会导致某个功能凌晨报错。更好的做法是通过 Tomcat 的 JNDI 数据源来管理连接应用只需从容器里取一个 DataSource改配置只动一个文件。在 Tomcat 8.5 里可以在META-INF/context.xml中这样配置?xml version1.0 encodingUTF-8? Context Resource namejdbc/userdb authContainer typejavax.sql.DataSource maxTotal20 maxIdle8 usernameroot passwordyourpassword driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/userdb?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8/ /Context参数说明name是 JNDI 名称代码里通过lookup(java:comp/env/jdbc/userdb)获取。maxTotal是最大连接数maxIdle是空闲连接数这两个值决定数据库连接池的伸缩范围一般开发环境分别设 20 和 8 就够。driverClassName在 MySQL 8 以上的驱动里要写成com.mysql.cj.jdbc.Driver老代码里的com.mysql.jdbc.Driver在新版驱动里已经被标记为过时。url里的useSSLfalse针对本地开发环境避免 MySQL 8 默认要求 SSL 握手导致的链接警告serverTimezoneAsia/Shanghai解决时区不一致导致的时间字段偏差characterEncodingUTF-8是数据库连接层面的编码兜底。配套的代码侧获取方式是这样import javax.naming.InitialContext; import javax.sql.DataSource; public class DBUtil { public static Connection getConnection() throws Exception { InitialContext ctx new InitialContext(); DataSource ds (DataSource) ctx.lookup(java:comp/env/jdbc/userdb); return ds.getConnection(); } }逻辑说明这段代码不需要关心数据库地址和密码全部信息都在 context.xml 里。以后换数据库环境只改 XML不动 Java。你可能会问过渡到 Spring Boot 后不是有更简单的配置方式吗确实Spring Boot 用application.properties里的spring.datasource.*就够了但理解 JNDI 数据源能帮你读懂老代码为什么那样设计以及迁移时如何识别连接参数。这也是这套源码里少数“学了不过时”的知识点。4.3 中文乱码三件套JSP、Servlet、连接串缺一不可老项目里中文乱码几乎和“Hello World”一样常见而乱码的原因通常不是单点的而是链接路上三个环节编码不一致。常见的现状是页面能打开但表单提交的中文到数据库后变成问号或者从数据库读出来再显示就是乱码。排查时按下面三个位置逐一检查。第一处是 JSP 页面头的编码声明% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %contentType里的charset告诉浏览器以 UTF-8 渲染pageEncoding告诉容器这个 JSP 文件本身用什么编码读取。两个都要写只写一个会出现“文件是 UTF-8但容器按 ISO-8859-1 解析”的错位。第二处是 Servlet 里接 request 参数前设置编码request.setCharacterEncoding(UTF-8);注意这行必须放在第一个getParameter()之前放在之后就没有效果了。Tomcat 8 之后GET 请求的默认 URI 编码仍然是 ISO-8859-1如果 GET 参数里带中文需要在server.xml的 Connector 上加上URIEncodingUTF-8。POST 请求则主要靠setCharacterEncoding来控制。第三处是数据库连接串和表结构。连接串里的characterEncodingUTF-8刚才已经提过同时要确认数据库表字段的字符集是 utf8mb4而不是 latin1。排查时可以执行SHOW CREATE TABLE user;重点看DEFAULT CHARSET那一行。如果显示 latin1即使 Java 端全改成 UTF-8存储层还是会丢字符。解决办法是改表字符集ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4;执行完之后重新跑一次写入和读取数据里的中文就能正常存取了。很多项目改了十几处代码还是乱码最后发现是表结构的问题。我的习惯是遇到乱码先看数据库字符集再回头查代码顺序不对很容易白折腾。5. 避坑手册这套 J2EE 旧源码最常见的五个翻车现场老代码最折磨人的地方不是业务逻辑有多难而是那些看似无关紧要的环境差异。下面五条全部来自实际交付过程中反复出现的现象每一条我都按“现象、原因、解决”的顺序拆开写。5.1 现象导入 Eclipse 后项目上全是红叉原因缺servlet-api.jar、jsp-api.jar这类容器专属依赖。源码包里通常不会把这些 jar 塞进WEB-INF/lib因为它期望运行时由 Tomcat 提供。Eclipse 里没有关联 Tomcat 运行环境编译器就找不到javax.servlet.http.HttpServlet等类于是每个 import 都报错。解决在 Eclipse 里给项目设置 Target Runtime。右键项目 Properties选择 Targeted Runtimes勾选已经配置好的 Tomcat 8.5点 Apply。完成后让 Eclipse 重新校验项目红叉应该基本消失。如果还缺别的第三方包检查项目的lib目录是否在 Build Path 里不在就通过 Configure Build Path 手动添加。这一步解决不了的话还有一个退路确认WEB-INF/lib下需要的 jar 是否完整比如 MySQL 驱动、日志包。5.2 现象EJB 相关的类找不到或者启动时报Cannot find Home interface原因老教材的源码里可能包含 EJB 模块但 EJB 容器在 Tomcat 里是残缺的Tomcat 本身不提供完整 EJB 容器。如果你的源码包里有 EJB 代码又在普通 Tomcat 里部署就会遇到 Home 接口、JNDI 绑定相关的错误。解决判断这套源码里的 EJB 是重点还是附带。如果整本教材主要讲 JSP/ServletEJB 部分通常只做简介那么在本地验证时直接把 EJB 相关的类从构建路径里移除或者注释掉对应调用。如果确实需要跑通 EJB常见做法是换用 JBoss 或 WildFly 这类完整 Java EE 容器但这会显著增加环境搭建成本不太适合只想快速学习的学生。我的建议是初学者先跳 EJB把 Servlet 与 JSP 链路跑通EJB 概念等用到时再回头看。5.3 现象Tomcat 能正常启动但访问页面一直 404原因绝大多数情况是 context root 和你访问的 URL 对不上。比如 war 包名为 myapp但访问时写成了http://localhost:8080/myapp/login.jsp中某个路径写错或者在 Eclipse 里部署时的 context root 被改成了别的名字。另一个隐藏原因是welcome-file-list里写的文件本身不存在。解决先确认应用名再确认请求路径。直接访问http://localhost:8080/看 Tomcat 首页再访问http://localhost:8080/myapp/如果返回 Tomcat 的 404 页面并提示“The requested resource is not available”说明 context root 不对。打开 Tomcat 的webapps目录看解压出的文件夹名字访问路径完全以它为准。在老项目里还要检查server.xml的Context配置有没有单独设置docBase和path如果配置了Context path/myapp docBase...那么路径由path属性决定而不是文件夹名。把这两处对齐404 基本能解决。5.4 现象表单提交的中文到 Servlet 里打印出来是乱码原因三层编码不一致。JSP 页面用 GBK 保存pageEncoding却写了 UTF-8或者 Servlet 没有在取参数前设置request.setCharacterEncoding(UTF-8)又或者浏览器提交的请求头里没带 charset容器按默认编码解析。解决按前面“中文乱码三件套”的思路逐一排查。先用file命令确认 JSP 文件的实际编码再确认页面指令里的pageEncoding和它一致。确认 Servlet 里setCharacterEncoding放在getParameter之前。最后在 web.xml 里加一个全局过滤器统一处理编码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 /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping这里用的是 Spring 提供的过滤器如果项目里没引 Spring可以自己写一个实现Filter接口的类逻辑就是强制 request 和 response 的编码为 UTF-8再链式调用chain.doFilter。三步处理完再测一次乱码应该消失。5.5 现象能编译能部署但页面一访问数据库就报Communications link failure或ClassNotFoundException: com.mysql.jdbc.Driver原因老代码里写死了老驱动类和老连接串。com.mysql.jdbc.Driver在 MySQL 5.x 时代是标准写法但 MySQL 8 的官方驱动把它改名成com.mysql.cj.jdbc.Driver并且要求连接串里带上时区参数。两个问题叠一起就是数据库完全没有连上。解决把代码和 XML 里的驱动类统一改成新驱动连接串加上useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。具体可以参考前面 context.xml 里的写法。同时确认WEB-INF/lib下放的是新版本的 mysql-connector-j jar而不是被拷贝了多次的旧包。如果项目里同时存在多个版本的驱动 jar建议只保留最新版避免类冲突导致驱动加载到旧类。改完之后重启 Tomcat 再测试读取注意连接池里可能有缓存连接改完配置最好重启容器而不是只热发布。6. 进阶把 J2EE 老代码迁到 Spring Boot按模块复用的三个技巧如果这套源码你已经能稳定跑通下一步值得做的事是把它迁移到一个现代工程里。迁移不是为了炫技而是为了让你真正理解 J2EE 和 Spring Boot 在分层思想上的一脉相承。老代码里的 JavaBean 和 DAO 逻辑几乎可以原样搬进 Service 层Servlet 里的控制逻辑替换成 Controller 里的方法JSP 则可以保留一段时间等你熟悉 Thymeleaf 之后再替换。第一个技巧是先把数据访问层从 Servlet 里剥离。老代码里经常直接在 Servlet 里写数据库操作迁移时要统一搬到 Service 或 DAO 类里。比如前面那个UserBean迁移后可以改成一个UserService方法签名保持不变调用方从 LoginServlet 换成 LoginControllerService public class UserService { public boolean checkLogin(String username, String password) { // 使用 MyBatis 或 Spring Data JPA 实现同一段查询逻辑 return userRepository.countByUsernameAndPassword(username, password) 0; } }第二个技巧是 Servlet 映射改成 Controller 注解。Tomcat 里的servlet-mapping配置对应的是 Spring MVC 里的PostMappingController public class LoginController { Autowired private UserService userService; PostMapping(/login.do) public String login(String username, String password, HttpSession session, Model model) { if (userService.checkLogin(username, password)) { session.setAttribute(loginUser, username); return redirect:/welcome.jsp; } else { model.addAttribute(error, 用户名或密码错误); return login; } } }关键点在于/login.do延续了原来的访问路径页面里的表单不用改动。redirect:对应原来的sendRedirect返回视图名对应原来的forward。第三个技巧是数据源配置合并到 application.properties。迁移后context.xml 里的 JNDI 数据源不再需要直接写spring.datasource.urljdbc:mysql://localhost:3306/userdb?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8 spring.datasource.usernameroot spring.datasource.passwordyourpassword spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.hikari.maximum-pool-size20迁移验证的方法是建一个空壳 Spring Boot 工程配置好数据源和依赖先把不带界面的 Service 层迁过去写一个简单的单元测试调用登录校验确认返回结果一致再迁 Controller保留 JSP 视图最后删除旧的 war 部署用 Spring Boot 内嵌 Tomcat 启动重新回归一遍登录流程。注意迁移 JSP 时Spring Boot 需要额外引入 JSP 解析依赖并在application.properties里配置视图前缀和后缀否则页面会 404。几年前我第一次动手迁移这种老项目时犯过最蠢的错误是把整个 web.xml 复制进 Spring Boot结果容器把老的过滤器加载了两遍应用启动直接抛异常。从那以后我养成了一个习惯迁移时每个配置文件都重新检查一遍不直接复制旧文件只借用里面的参数。这也是我希望你在这套源码上能练出来的能力——读懂的代码才值得被复用。希望帮到你。本文还有配套的精品资源点击获取