ARTICLE DETAIL

资讯详情

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

Spring Boot项目迁移到东方通TongWeb实战指南

Spring Boot项目迁移到东方通TongWeb实战指南 1. 项目概述为什么Spring Boot项目要“换掉Tomcat”偏偏选东方通TongWeb信创这个词这两年在政企、金融、能源这些行业的技术会议上几乎每场必提。但很多人一听到“信创适配”第一反应还是——又要改代码又要重测又要加班其实真没那么吓人尤其当你用的是Spring Boot这种高度抽象的框架时底层Servlet容器的切换本质上是一次“拔插式”替换不是推倒重来。关键在于你得知道插哪里、怎么插、插完怎么验。而东方通TongWeb就是国产中间件里目前落地最稳、文档最全、生态对接最成熟的那一款。我去年接手一个省级医保平台的二期改造原系统是Spring Boot 2.6.x Tomcat 9要求半年内完成信创环境迁移目标运行环境是麒麟V10 飞腾2000处理器 东方通TongWeb V7.0。当时团队里有位老哥直接说“Spring Boot打包成WAR丢到TongWeb里不就完了”结果部署上去首页404日志里全是java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener。后来才发现他连TongWeb的JDK版本要求都没看——TongWeb V7.0官方只支持JDK 8u261及以上或JDK 11而他本地用的是JDK 17连启动都报错。这事儿让我意识到信创不是换个名字喊口号它是一整套兼容性链条从JVM、类加载机制、线程模型到管理控制台的交互逻辑全都得重新对齐。所以这篇内容不讲政策文件不列长串适配清单就聚焦一个最实际的问题一个标准的Spring Boot Web项目Maven构建、内嵌Tomcat默认配置如何零修改核心业务代码仅通过调整构建方式、依赖和少量配置就能在TongWeb上跑起来并能进管理后台查应用状态、看日志、启停服务我会把整个过程拆成四步先搞懂TongWeb和Tomcat的根本差异在哪再手把手带你改pom.xml、改启动类、打WAR包然后教你怎么在Linux服务器上静默安装TongWeb、配置JDK、部署应用最后给你一份真实压测场景下的调优参数和三个我踩过的坑——比如为什么你的Spring Boot Actuator端点在TongWeb里返回500为什么MyBatis的SQL日志死活不打印为什么上传大文件时总卡在80%不动。这些细节官网文档不会写但你在现场一定会遇到。2. 核心设计思路不是“替换”而是“解耦适配”2.1 Spring Boot的Servlet容器抽象层才是我们真正的杠杆很多人以为Spring Boot“内置Tomcat”就意味着和Tomcat强绑定这是个巨大误解。Spring Boot真正依赖的从来不是Tomcat本身而是Servlet规范3.1和Java EE现Jakarta EE的Web容器契约。Tomcat、Jetty、Undertow甚至TongWeb、金蝶Apusic、宝兰德BES只要它们实现了Servlet 3.1规范Spring Boot就能和它们“握手”。这个握手协议就是spring-boot-starter-web里的ServletWebServerFactory接口。我们来看一段Spring Boot启动时的真实日志2024-06-15 10:23:45.123 INFO 12345 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)这行日志里的关键词是TomcatWebServer但它只是ServletWebServerFactory的一个实现。如果你把spring-boot-starter-tomcat这个starter从依赖里去掉Spring Boot启动时就会报错“No ServletWebServerFactory bean found”。但如果你自己提供一个TongWebServletWebServerFactory当然东方通没提供这个我们也不需要或者更现实的做法——放弃内嵌容器走传统WAR部署模式那整个问题就从“如何替换内嵌容器”降维成“如何让Spring Boot应用符合Servlet容器的部署规范”。这就是整个方案的设计原点不硬刚TongWeb的内嵌集成而是回归Servlet标准用最成熟、最可控的WAR包方式交付。这样做的好处是三点第一完全规避了TongWeb对Spring Boot内嵌容器的兼容性黑盒第二部署流程和原有Tomcat环境几乎一致运维同学不用学新命令第三所有Spring Boot特性自动配置、Actuator、Profile全部保留你只是换了个“房子”没换“家具”。2.2 TongWeb和Tomcat的三大本质差异决定了我们必须改什么光知道要打WAR包还不够。TongWeb不是Tomcat的“国产马甲”它在底层实现上有三处关键差异直接影响你的Spring Boot项目能否顺利启动第一类加载器模型不同。Tomcat采用经典的双亲委派模型Parent-Delegation应用类优先从WEB-INF/lib加载找不到才向上委托给Common ClassLoader。而TongWeb V7.0默认采用逆向委派Child-First模式即先从应用自己的WEB-INF/lib找类找不到才去父加载器如System ClassLoader里找。这听起来是好事但恰恰是很多ClassNotFoundException的根源。比如Spring Boot的spring-boot-autoconfigure里有个DataSourceAutoConfiguration类它依赖javax.sql.DataSource这个接口在JDK里也在TongWeb的lib目录下。如果TongWeb的类加载器先从自己的lib里加载了一个旧版javax.sql比如JDK 8的而你的Spring Boot用的是JDK 11的API就会出现IncompatibleClassChangeError。解决方案不是改代码而是在TongWeb的server.xml里显式关闭逆向委派或者更稳妥地在WAR包的WEB-INF/web.xml里加一行loader delegatetrue/强制走标准双亲委派。第二JNDI资源绑定机制不同。Tomcat里配置数据源你习惯写context.xml然后在Spring里用Resource(namejdbc/mydb)注入。TongWeb也支持JNDI但它的JNDI命名空间是java:comp/env/而且默认不启用全局JNDI。如果你的Spring Boot项目里用了ConfigurationProperties(prefixspring.datasource.jndi)在TongWeb里很可能查不到JNDI Context。实测下来最稳的方式是放弃JNDI改用Spring Boot原生的application.yml配置数据源把连接池HikariCP的配置项全写明这样既脱离容器依赖又便于多环境切换。第三管理控制台的权限体系和URL路径完全不同。Tomcat的管理界面是/manager/html用户名密码在tomcat-users.xml里配。TongWeb的管理后台默认地址是http://localhost:6888/console初始账号是admin/tongweb注意不是tongweb123网上很多教程写错了而且首次登录后强制改密。更重要的是TongWeb的“应用部署”功能不叫“Deploy”叫“应用发布”它不接受直接上传WAR包而是要求你先把WAR包放到webapps目录下再在控制台里点“发布”按钮或者用命令行工具twctl。这点新手极易卡住——你传了WAR包刷新页面却看不到应用列表因为没点“发布”。这三点差异就是我们后续所有操作的底层逻辑。记住不是TongWeb“不兼容”Spring Boot而是它遵循了一套略有不同的企业级中间件规范。我们的任务是做适配不是做对抗。3. 实操全流程从Spring Boot项目到TongWeb成功运行3.1 第一步改造Spring Boot项目生成标准WAR包假设你有一个标准的Spring Boot Web项目结构如下my-springboot-app/ ├── pom.xml ├── src/ │ └── main/ │ ├── java/com/example/app/ │ │ └── MyApplication.java │ └── resources/application.yml └── target/my-springboot-app-1.0.0.jar现在我们要把它变成能在TongWeb里跑的WAR包。核心改动只有三处全部在pom.xml和启动类里业务代码一行都不用动。第一步修改pom.xml移除内嵌Tomcat添加WAR打包支持找到pom.xml里的spring-boot-starter-web依赖把它改成dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId !-- 关键排除内嵌Tomcat -- exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId /exclusion /exclusions /dependency !-- 添加Servlet API依赖这是WAR包必需的 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId scopeprovided/scope /dependency然后在pom.xml的packaging标签里把jar改成warpackagingwar/packaging最后添加spring-boot-starter-tomcat为provided范围注意这里不是为了用它而是为了让Spring Boot的编译期能识别Servlet APIdependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId scopeprovided/scope /dependency提示provided范围意味着这个依赖只在编译和测试时有效打包时不会打进WAR包避免和TongWeb自带的Servlet实现冲突。这是Maven WAR打包的黄金法则。第二步改造启动类继承SpringBootServletInitializer打开你的主启动类MyApplication.java让它继承SpringBootServletInitializer并重写configure方法SpringBootApplication public class MyApplication extends SpringBootServletInitializer { Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { // 这里必须return builder.sources(MyApplication.class)不能写错类名 return builder.sources(MyApplication.class); } public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }这个configure方法是关键。它告诉Spring Boot“我不是独立运行的Jar我是要被Servlet容器加载的WAR请按Web应用的方式初始化上下文。”没有这一步TongWeb启动时会找不到Spring的根上下文所有Controller、Service都会失效。第三步验证打包生成WAR文件执行Maven命令mvn clean package -Dmaven.test.skiptrue你会在target/目录下看到my-springboot-app-1.0.0.war。用jar -tf target/my-springboot-app-1.0.0.war | head -20检查一下结构确认里面有WEB-INF/classes/你的编译字节码WEB-INF/lib/所有依赖jar包括spring-boot-starter-webWEB-INF/web.xmlSpring Boot自动生成的不用手动创建注意Spring Boot 2.3版本默认不生成web.xml这是完全OK的。Servlet 3.0规范支持注解驱动TongWeb V7.0完全支持。如果你的项目用了老版本Spring Boot2.3建议升级否则可能需要手写web.xml。3.2 第二步在Linux服务器上静默安装TongWeb V7.0TongWeb的安装包是.bin格式官网下载后需要在目标服务器麒麟V10 飞腾CPU上执行。这里强调“静默安装”因为生产环境通常不允许图形界面且要写入自动化脚本。前提检查JDK版本与环境变量TongWeb V7.0官方支持JDK 8u261 或 JDK 11。我们以JDK 11为例推荐因Spring Boot 2.7.x对JDK 11优化更好# 查看当前JDK java -version # 输出应为 openjdk version 11.0.22 2024-04-16 # 确认JAVA_HOME指向正确路径 echo $JAVA_HOME # 应输出 /usr/lib/jvm/java-11-openjdk-arm64 飞腾架构需用arm64版本 # 如果未设置编辑 /etc/profile sudo vi /etc/profile # 在末尾添加 export JAVA_HOME/usr/lib/jvm/java-11-openjdk-arm64 export PATH$JAVA_HOME/bin:$PATH source /etc/profile执行静默安装下载的安装包假设为tongweb-v7.0-linux-aarch64.bin注意aarch64是飞腾CPU的架构标识# 赋予执行权限 chmod x tongweb-v7.0-linux-aarch64.bin # 创建安装目录 sudo mkdir -p /opt/tongweb # 执行静默安装-i silent 是关键参数 sudo ./tongweb-v7.0-linux-aarch64.bin -i silent -DINSTALLDIR/opt/tongweb -DJAVA_HOME/usr/lib/jvm/java-11-openjdk-arm64安装完成后TongWeb的主目录是/opt/tongweb核心目录结构如下/opt/tongweb/ ├── bin/ # 启动/停止脚本start.sh, stop.sh, twctl ├── conf/ # 主配置server.xml, web.xml ├── lib/ # TongWeb核心jar包 ├── logs/ # 日志文件 ├── webapps/ # 应用部署目录WAR包放这里 └── console/ # 管理控制台静态资源启动TongWeb并验证# 切换到bin目录 cd /opt/tongweb/bin # 启动后台运行 ./start.sh # 查看进程 ps -ef | grep tongweb # 应看到类似/usr/lib/jvm/java-11-openjdk-arm64/bin/java ... -Dcatalina.home/opt/tongweb ... # 查看日志确认启动成功 tail -f /opt/tongweb/logs/stdout.log # 等待出现 Server startup in [xxx] milliseconds 即成功此时访问http://你的服务器IP:6888/console用默认账号admin/tongweb登录。首次登录后系统会强制你修改密码新密码需包含大小写字母数字特殊字符8位以上。改完密码你就进入了TongWeb的管理后台。3.3 第三步部署WAR包配置应用参数上传WAR包到webapps目录用scp或rz命令把本地生成的my-springboot-app-1.0.0.war上传到服务器scp target/my-springboot-app-1.0.0.war userserver-ip:/opt/tongweb/webapps/上传后不要急着去控制台点“发布”。先检查webapps目录ls -l /opt/tongweb/webapps/ # 你会看到 my-springboot-app-1.0.0.war 文件以及一个同名的空文件夹 my-springboot-app-1.0.0/ # 这是TongWeb自动解压的痕迹说明它已识别到WAR包在管理控制台发布应用登录http://server-ip:6888/console。左侧菜单栏点击“应用管理” → “应用发布”。在右侧列表中找到my-springboot-app-1.0.0注意显示的是解压后的文件夹名不是WAR包名。勾选它点击上方的“发布”按钮。弹窗确认点击“确定”。发布成功后状态会从“未发布”变成“已发布”但此时应用还是“停止”状态。你需要再选中它点击“启动”按钮。注意TongWeb的“发布”和“启动”是两个独立操作。只发布不启动应用不会运行只启动未发布会报错“应用未发布”。配置应用JVM参数关键调优Spring Boot应用在TongWeb里跑内存不足是常见问题。默认JVM参数太小-Xms256m -Xmx512m对于一个带MyBatis和Redis的中型应用远远不够。我们需要修改/opt/tongweb/bin/setenv.sh# 编辑setenv.sh vi /opt/tongweb/bin/setenv.sh # 找到JAVA_OPTS行修改为根据你的服务器内存调整 JAVA_OPTS-Xms2g -Xmx4g -XX:MetaspaceSize512m -XX:MaxMetaspaceSize1g -XX:UseG1GC -XX:MaxGCPauseMillis200然后重启TongWeb./stop.sh ./start.sh验证应用是否正常运行启动后查看/opt/tongweb/logs/stdout.log搜索my-springboot-app应看到类似日志2024-06-15 14:30:22.123 INFO 12345 --- [main] com.example.app.MyApplication : Started MyApplication in 25.432 seconds (JVM running for 28.765)再访问应用URLhttp://server-ip:6888/my-springboot-app-1.0.0/注意上下文路径默认是WAR包名不含.war后缀。如果看到你的首页恭喜第一步成功3.4 第四步打通Actuator、日志、文件上传等关键能力Spring Boot的便利性很大一部分来自Actuator健康检查、Logback日志、RequestBody文件上传等。这些在TongWeb里不是开箱即用需要针对性配置。启用并访问Actuator端点在application.yml里确保开启了Actuatormanagement: endpoints: web: exposure: include: * # 或者明确列出 health,info,metrics,loggers endpoint: health: show-details: always但直接访问http://server-ip:6888/my-springboot-app-1.0.0/actuator/health可能返回404或500。原因有两个一是TongWeb的Servlet映射规则二是Actuator的WebMvcEndpointHandlerMapping需要额外配置。解决方案在application.yml里加一行spring: web: resources: static-locations: classpath:/static/,classpath:/public/ mvc: static-path-pattern: /static/** # 关键指定Actuator的base-path management: endpoints: web: base-path: /actuator这样Actuator端点就固定在/actuator/health而不是被TongWeb的默认Servlet拦截。让MyBatis SQL日志在TongWeb里正常打印很多开发者发现logging.level.org.mybatisDEBUG在TongWeb里不生效。这是因为TongWeb自身的日志框架Log4j2和Spring Boot的Logback存在冲突。解决方法是统一用Logback并禁用TongWeb的Log4j2。在src/main/resources/下创建logback-spring.xml?xml version1.0 encodingUTF-8? configuration include resourceorg/springframework/boot/logging/logback/defaults.xml/ property nameLOG_FILE value${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/logs}/spring.log}/ appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern${CONSOLE_LOG_PATTERN}/pattern /encoder /appender !-- 关键为MyBatis开启DEBUG -- logger nameorg.mybatis levelDEBUG additivityfalse appender-ref refCONSOLE/ /logger root levelINFO appender-ref refCONSOLE/ /root /configuration然后在pom.xml里排除TongWeb自带的Log4j2dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-logging/artifactId /exclusion /exclusions /dependency !-- 显式引入Logback -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-log4j2/artifactId exclusions exclusion groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-core/artifactId /exclusion /exclusions /dependency解决大文件上传413错误Spring Boot默认单文件上传限制是1MBTongWeb默认也是1MB。当用户上传大于1MB的文件时会返回HTTP 413。需要两边同时调大在application.yml里spring: servlet: multipart: max-file-size: 50MB max-request-size: 50MB在TongWeb的/opt/tongweb/conf/server.xml里找到Connector标签添加maxPostSize属性Connector port6888 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 maxPostSize52428800 / !-- 50MB 52428800 bytes --重启TongWeb问题解决。4. 常见问题排查与独家避坑指南4.1 问题速查表从404到500一线排障记录现象可能原因排查步骤解决方案首页404但TongWeb日志无报错WAR包未发布或上下文路径错误1. 登录控制台确认应用状态为“已启动”2.ls /opt/tongweb/webapps/确认解压后的文件夹存在3. 访问URL是否带了.war后缀错误/app.war/正确/app/在控制台“应用发布”里发布并启动应用URL去掉.war启动时报ClassNotFoundException: org.springframework.web.context.ContextLoaderListenerspring-boot-starter-web依赖未排除Tomcat或javax.servlet-api范围不是provided1.jar -tf target/app.war | grep servlet-api确认javax.servlet-api没被打包进去2. 检查pom.xml确认spring-boot-starter-tomcat被exclusion且scopeprovided严格按3.1节修改pom.xml重新打包Actuator/actuator/health返回500日志报NoSuchBeanDefinitionExceptionWebMvcEndpointHandlerMapping未正确注册1.grep -r WebMvcEndpointHandlerMapping /opt/tongweb/logs/2. 检查application.yml是否漏了management.endpoints.web.base-path在application.yml中显式设置management.endpoints.web.base-path: /actuatorMyBatis SQL不打印logging.level.org.mybatisDEBUG无效TongWeb的Log4j2与Logback冲突1.ps aux | grep java确认JVM参数里没有-Dlog4j.configurationFile2.cat /opt/tongweb/conf/log4j2.xml确认是否存在删除/opt/tongweb/conf/log4j2.xml或重命名为log4j2.xml.bak强制使用Logback上传文件时进度条卡在80%最终超时TongWeb的maxPostSize和Spring Boot的maxRequestSize不一致1.curl -X POST http://ip:6888/app/upload -F filelarge.zip观察响应时间2.tail -f /opt/tongweb/logs/stdout.log搜索timeout同时修改application.yml和server.xml中的大小限制保持一致4.2 三个血泪教训那些官网不会写的细节教训一TongWeb的“热部署”是把双刃剑TongWeb控制台有个“热部署”开关默认是开启的。意思是当你修改了webapps/app/WEB-INF/classes/下的class文件TongWeb会自动重新加载。这听起来很爽但对Spring Boot是灾难。因为Spring Boot的ApplicationContext是单例且复杂的热加载会导致Bean定义冲突、AOP代理失效、甚至内存泄漏。我亲眼见过一个项目热部署三次后/actuator/metrics返回的JVM内存指标翻了五倍最后OOM。我的做法在/opt/tongweb/conf/server.xml里把Context标签的reloadable属性设为falseContext path/myapp docBasemy-springboot-app-1.0.0 reloadablefalse /生产环境永远用“停-替-启”三步法更新代码别信热部署。教训二application.yml里的spring.profiles.active在TongWeb里可能失效Spring Boot默认从spring.profiles.active读取激活的Profile但在TongWeb里这个值有时会被容器的系统属性覆盖。比如TongWeb启动脚本里设置了-Dspring.profiles.activeprod就会覆盖你application.yml里的配置。我的做法不依赖application.yml改用JVM参数指定# 修改 /opt/tongweb/bin/start.sh在JAVA_OPTS里加 JAVA_OPTS$JAVA_OPTS -Dspring.profiles.activeprod这样Profile的优先级最高且所有环境一致。教训三TongWeb的/console管理后台千万别用Chrome最新版直连这不是Bug是兼容性问题。TongWeb V7.0的控制台前端用的是较老的ExtJS框架而Chrome 120默认禁用了某些旧版JavaScript API如document.all。结果就是登录后页面白屏F12看Console全是ReferenceError。我的做法给运维同学发一条命令让他们在Chrome里输入chrome://flags/#unsafely-treat-insecure-origin-as-secure把Insecure origins treated as secure设为Enabled然后重启Chrome。或者更简单——用Firefox ESR企业版或Edge的IE模式访问http://ip:6888/console100%兼容。5. 后续可扩展方向不止于“能跑”更要“跑好”做到上面四步你的Spring Boot应用已经在TongWeb上稳定运行了。但这只是信创迁移的第一公里。接下来你可以沿着这三个方向深挖把项目做得更扎实第一集成TongWeb的集群能力。TongWeb支持多节点集群通过conf/tongweb-cluster.xml配置。你可以把Spring Boot的Session存储从内存改成Redis再配合TongWeb的集群广播实现真正的高可用。这比单纯堆机器更有技术含量。第二对接东方通的统一认证中心UAC。很多政企项目要求单点登录SSO。TongWeb提供了标准的CAS和OAuth2.0接入方式。你不需要重写登录逻辑只需在Spring Security里配置CasAuthenticationFilter或OAuth2LoginAuthenticationFilter把认证委托给UAC。第三利用TongWeb的APM监控探针。TongWeb自带性能监控模块可以采集JVM、SQL、HTTP请求的详细指标。你可以在conf/tongweb-monitor.xml里开启探针然后把数据推送到Prometheus用Grafana画出你的Spring Boot应用的实时性能大盘。这才是信创项目该有的技术水位。最后分享一个小技巧每次升级TongWeb小版本比如从V7.0.1到V7.0.2别急着全量上线。先用twctl命令行工具在测试环境部署一个最小化的Spring Boot Hello World WAR包跑通/actuator/health和/actuator/metrics确认基础能力没问题再上业务应用。信创落地稳字当头快不如准。
返回列表