1. 从“Hello World”到“真实世界”:SpringBoot项目实战的思维跃迁
如果你已经跟着前两天的内容,成功跑通了第一个SpringBoot应用,看到了那个经典的“Hello World”页面,那么恭喜你,你已经迈出了坚实的第一步。但我想说的是,这仅仅是开始。很多初学者会在这里陷入一个误区:以为学会了创建项目、写个Controller返回字符串,就算是入门SpringBoot了。实际上,从“玩具项目”到“具备生产级雏形的项目”,中间隔着一道需要主动跨越的鸿沟。Day03的目标,就是带你跨过这道鸿沟,不再满足于Demo,而是开始用工程化的思维去构建一个“真实”的SpringBoot应用。我们会聚焦于那些在教程里常常一笔带过,但在实际开发中天天要打交道的核心配置、集成与部署问题。当你开始思考“我的配置文件该怎么组织?”、“日志怎么打才规范?”、“如何连接真实的数据库?”时,你的SpringBoot学习才真正进入了快车道。
2. 项目骨架的深化:超越默认的application.properties
当我们用Spring Initializr创建项目时,会得到一个默认的application.properties(或application.yml)文件。很多人就直接在这里面堆砌所有配置,项目稍大一点,这个文件就会变得臃肿不堪,难以维护。一个清晰的配置管理策略,是项目可维护性的基石。
2.1 多环境配置:让应用适应不同舞台
任何严肃的项目都需要区分环境:本地开发(dev)、测试(test)、预发布(staging)、生产(prod)。SpringBoot对此提供了优雅的支持。
核心机制:ProfileProfile可以理解为一系列配置的“分组”。你可以为每个环境创建独立的配置文件,命名规则为application-{profile}.properties/yml。例如:
application-dev.yml:开发环境配置application-test.yml:测试环境配置application-prod.yml:生产环境配置
而通用的、与环境无关的配置则放在主配置文件application.yml中。如何激活特定Profile呢?有几种常见方式:
命令行参数:在启动Jar包时指定。这是最灵活、在服务器上最常用的方式。
java -jar your-app.jar --spring.profiles.active=prod系统环境变量:在服务器上设置
SPRING_PROFILES_ACTIVE环境变量。export SPRING_PROFILES_ACTIVE=prodIDE配置:在IDEA的Run/Debug Configuration中,VM options或Program arguments里添加。
-Dspring.profiles.active=dev 或 --spring.profiles.active=dev
实操心得:配置的优先级与覆盖SpringBoot的配置源是有优先级的。高优先级的配置会覆盖低优先级的。一个典型的优先级顺序(从高到低)是:
- 命令行参数(
--server.port=8081) SPRING_APPLICATION_JSON中的属性(内嵌的JSON环境变量)- ServletConfig初始化参数
- ServletContext初始化参数
- JNDI属性(
java:comp/env) - Java系统属性(
System.getProperties()) - 操作系统环境变量
application-{profile}.yml(带Profile的)application.yml(不带Profile的)@Configuration类上的@PropertySource注解- 默认属性(通过
SpringApplication.setDefaultProperties指定)
理解这个顺序非常重要。比如,生产环境的数据库密码,你肯定不想写在代码仓库里的application-prod.yml中。更安全的做法是:在application-prod.yml中配置一个占位符,然后在服务器上通过环境变量或命令行参数传入真实的密码。这样密码就不会进入代码版本库。
2.2 YAML vs Properties:不仅仅是语法糖
.yml(YAML)格式正在逐渐取代.properties,这是有原因的。YAML通过缩进来表示层级关系,对于复杂结构(如列表、嵌套对象)的配置,其可读性远胜于Properties文件。
Properties方式(繁琐且易错):
spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=10YAML方式(清晰直观):
spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver hikari: connection-timeout: 30000 maximum-pool-size: 10YAML的层级结构一目了然,特别是在配置如List或Map时优势更明显。个人建议:除非有历史包袱,新项目一律使用YAML格式。在IDEA中编写YAML时,可以利用其强大的自动补全和语法高亮功能。
2.3 自定义配置与@ConfigurationProperties
我们不仅会读取SpringBoot的配置,更多时候需要定义自己的配置。例如,定义一个文件上传的路径和大小限制。
步骤1:在application.yml中定义配置
myapp: upload: path: /var/www/uploads/ max-size: 10MB allowed-types: jpg,png,pdf步骤2:创建配置属性绑定类强烈推荐使用@ConfigurationProperties注解,而不是简单的@Value。@ConfigurationProperties支持类型安全的绑定、验证和宽松的绑定规则(如max-size可以绑定到Java字段maxSize)。
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Positive; import java.util.List; @Component @ConfigurationProperties(prefix = "myapp.upload") // 前缀匹配 public class UploadProperties { @NotEmpty private String path; @Positive private DataSize maxSize; // 使用Spring的DataSize类型,自动转换"10MB" private List<String> allowedTypes; // 标准的getter和setter方法必须提供 public String getPath() { return path; } public void setPath(String path) { this.path = path; } // ... 其他getter/setter }步骤3:在需要的地方注入使用
@Service public class FileService { private final UploadProperties uploadProperties; // 通过构造器注入 public FileService(UploadProperties uploadProperties) { this.uploadProperties = uploadProperties; } public void handleUpload() { System.out.println("上传路径: " + uploadProperties.getPath()); System.out.println("最大文件: " + uploadProperties.getMaxSize().toMegabytes() + " MB"); // 直接使用配置,类型安全,无需手动解析字符串 } }为什么推荐@ConfigurationProperties而不是@Value?
- 类型安全:自动将
String转换为Integer、Boolean、DataSize甚至自定义对象。 - 宽松绑定:支持
kebab-case(max-size)、camelCase(maxSize)、snake_case(max_size)等多种属性名风格,自动匹配到Java字段。 - 验证:可以结合JSR-303注解(如
@NotEmpty,@Min)在绑定时就进行校验。 - IDE支持:在IDEA中,如果你添加了
spring-boot-configuration-processor依赖,编写YAML时会有自动补全提示,极大提升开发体验。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
3. 数据访问层搭建:MyBatis-Plus的优雅集成
当项目需要持久化数据时,我们就要引入数据库。这里我选择介绍MyBatis-Plus,因为它是在原生MyBatis基础上做了极大增强的“神器”,能让你用极少的代码完成绝大部分CRUD操作,同时保留了MyBatis的灵活性。
3.1 依赖引入与基础配置
首先,在pom.xml中添加依赖。注意版本号,建议使用与SpringBoot版本兼容的稳定版。
<dependencies> <!-- SpringBoot JDBC 支持 (包含HikariCP连接池) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- MyBatis-Plus 启动器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.6</version> <!-- 请使用最新稳定版 --> </dependency> </dependencies>接着,在application.yml中配置数据源。这里以MySQL为例,并展示一些HikariCP连接池的优化参数。
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: your_username password: your_password hikari: # 连接池名称 pool-name: MyHikariCP # 最小空闲连接数 minimum-idle: 5 # 最大连接数,根据应用负载调整,一般建议公式:CPU核心数 * 2 + 有效磁盘数 maximum-pool-size: 20 # 连接最大存活时间(毫秒),MySQL默认wait_timeout是8小时,建议略小于此值 max-lifetime: 28740000 # 连接超时时间(毫秒) connection-timeout: 30000 # 测试连接有效性的查询语句 connection-test-query: SELECT 13.2 实体、Mapper与Service的极简开发
假设我们有一个User用户表。传统的MyBatis需要写XML映射文件,而MyBatis-Plus通过注解和继承,让这一切变得异常简单。
1. 实体类(Entity)使用@TableName指定表名,@TableId指定主键及策略(如自增)。
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; // 使用Lombok简化代码 @Data @TableName("sys_user") public class User { @TableId(type = IdType.AUTO) // 主键自增 private Long id; private String username; private String email; private Integer age; // 省略 getter/setter (由Lombok的@Data注解自动生成) }2. Mapper接口只需继承MyBatis-Plus提供的BaseMapper接口,即可获得全套CRUD方法,无需编写XML。
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper // 关键!让Spring能扫描到这个Mapper public interface UserMapper extends BaseMapper<User> { // 不需要写任何方法,BaseMapper已经提供了: // insert, deleteById, updateById, selectById, selectList, selectPage ... // 如果需要复杂查询,可以在这里定义方法,并配合@Select等注解或XML }3. Service层同样,可以继承IService和其实现类ServiceImpl,获得更强的服务层封装。
import com.baomidou.mybatisplus.extension.service.IService; public interface UserService extends IService<User> { // 可以在此定义业务特有的方法 User getByUsername(String username); }import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Override public User getByUsername(String username) { // 使用MyBatis-Plus的QueryWrapper构建查询条件 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", username); // getOne 方法获取一条记录 return this.getOne(queryWrapper); } }4. Controller层调用
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; // 分页查询 @GetMapping public Page<User> list(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { Page<User> page = new Page<>(pageNum, pageSize); return userService.page(page); } // 条件查询 @GetMapping("/search") public List<User> search(@RequestParam String keyword) { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.like("username", keyword).or().like("email", keyword); return userService.list(wrapper); } // 新增 @PostMapping public Boolean save(@RequestBody User user) { return userService.save(user); } // 更新、删除等操作类似... }踩坑实录:@Mapper注解与扫描路径如果你在Controller里注入Mapper时遇到NoSuchBeanDefinitionException,最常见的原因有两个:
- 忘记在Mapper接口上添加
@Mapper注解。 - 或者,你更倾向于使用包扫描。可以在主启动类上添加
@MapperScan("com.yourpackage.mapper")注解,指定Mapper接口所在的包路径。这样,该包下的所有Mapper接口都会被自动注册为Bean,无需每个接口都加@Mapper。个人建议使用@MapperScan,更清晰统一。
3.3 高级特性:分页插件与逻辑删除
MyBatis-Plus的插件机制非常强大,只需简单配置即可启用高级功能。
分页插件配置在配置类中(或主启动类)配置分页插件,否则Page对象不会生效。
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 还可以添加其他插件,如乐观锁插件 // interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }配置后,在Service中调用page(Page page, Wrapper wrapper)方法即可实现物理分页,SQL会自动添加LIMIT语句。
逻辑删除逻辑删除是指用字段标记记录被删除,而非物理删除数据。MyBatis-Plus对此有开箱即用的支持。
- 在表中添加一个删除标记字段,如
deleted,类型为tinyint或int,默认值为0。 - 在实体类中对应字段上添加
@TableLogic注解。@Data @TableName("sys_user") public class User { // ... 其他字段 @TableLogic private Integer deleted; // 0-未删除,1-已删除 } - 在
application.yml中配置逻辑删除的全局值(可选,新版通常不需要)。mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局逻辑删除的实体字段名 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值
配置完成后,当你调用userService.removeById(1)时,执行的不再是DELETE FROM user WHERE id=1,而是UPDATE user SET deleted=1 WHERE id=1 AND deleted=0。所有的select操作也会自动加上AND deleted=0条件。这极大地简化了逻辑删除的实现。
4. 应用日志:SLF4J与Logback的实战配置
日志是应用的“眼睛”,生产环境排查问题离不开它。SpringBoot默认使用Logback作为日志框架,并通过SLF4J门面进行调用。但默认配置可能不符合生产要求,我们需要进行定制。
4.1 理解日志级别与门面模式
日志级别从低到高分为:TRACE<DEBUG<INFO<WARN<ERROR。设置某个级别后,只会打印该级别及更高级别的日志。开发环境可以设为DEBUG,生产环境通常设为INFO或WARN。
SLF4J(Simple Logging Facade for Java)是一个日志门面,它不负责具体的日志实现,只是提供统一的API。你的代码里应该只调用SLF4J的接口(如LoggerFactory.getLogger)。这样,底层无论是用Logback、Log4j2还是其他实现,都可以无缝切换。SpringBoot默认的Starter已经帮你做好了桥接。
4.2 定制Logback配置文件
SpringBoot允许在resources目录下放置一个logback-spring.xml文件来覆盖默认配置。使用-spring后缀可以让SpringBoot识别并应用其特有的扩展(如<springProfile>)。
一个功能齐全的生产级logback-spring.xml配置示例:
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="60 seconds"> <!-- 定义变量 --> <property name="LOG_HOME" value="./logs"/> <property name="APP_NAME" value="my-springboot-app"/> <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/> <!-- 控制台输出 (开发环境常用) --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 按天滚动的文件输出 (生产环境必备) --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/${APP_NAME}.log</file> <!-- 滚动策略:按日期+文件大小 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每日滚动,并保留30天历史,文件格式为 my-app-2024-01-01.log --> <fileNamePattern>${LOG_HOME}/${APP_NAME}-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <!-- 每个日志文件最大100MB --> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>30</maxHistory> <!-- 保留30天的日志 --> </rollingPolicy> <encoder> <pattern>${LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 错误日志单独输出到一个文件 --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/${APP_NAME}-error.log</file> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/${APP_NAME}-error-%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>${LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 根据不同Profile激活不同配置 --> <springProfile name="dev"> <root level="DEBUG"> <appender-ref ref="CONSOLE"/> </root> <!-- 将特定包(如Mapper)的日志级别调低,方便查看SQL --> <logger name="com.yourpackage.mapper" level="DEBUG"/> </springProfile> <springProfile name="prod"> <root level="INFO"> <appender-ref ref="FILE"/> <appender-ref ref="ERROR_FILE"/> <!-- 生产环境也可以输出到控制台,方便容器采集 --> <appender-ref ref="CONSOLE"/> </root> <!-- 生产环境关闭一些不必要或过于详细的日志 --> <logger name="org.springframework" level="WARN"/> <logger name="com.zaxxer.hikari" level="INFO"/> </springProfile> </configuration>关键配置解读:
RollingFileAppender:这是核心,确保日志文件不会无限增大。通过TimeBasedRollingPolicy实现按天归档,结合SizeAndTimeBasedFNATP实现按天+按大小滚动(例如每天一个文件,但单个文件超过100MB就切分)。maxHistory:设置保留的历史日志文件天数,防止磁盘被撑满。ThresholdFilter:用于ERROR_FILE,只记录ERROR及以上级别的日志,方便错误排查。<springProfile>:这是SpringBoot的扩展,可以根据激活的Profile使用不同的日志策略。开发环境侧重控制台输出和DEBUG信息;生产环境侧重文件输出、性能和信息安全。
4.3 在代码中优雅地记录日志
不要在代码里直接System.out.println,使用SLF4J的API。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RestController; @RestController public class SomeController { // 使用当前类作为Logger的名称,这是标准做法 private static final Logger log = LoggerFactory.getLogger(SomeController.class); public void someMethod() { // 使用占位符{},避免字符串拼接(在日志级别不够时,拼接操作是浪费的) String userId = "123"; log.info("用户 [{}] 尝试登录", userId); // 输出:用户 [123] 尝试登录 try { // 一些可能出错的业务 riskyOperation(); } catch (Exception e) { // 记录错误时,一定要将异常对象作为最后一个参数传入 log.error("处理用户 [{}] 请求时发生异常", userId, e); // 这样日志框架才能打印完整的堆栈跟踪信息 } // 对于需要复杂计算才能得到的日志信息,先判断级别,避免无效计算 if (log.isDebugEnabled()) { log.debug("一些复杂的调试信息: {}", expensiveCalculation()); } } private String expensiveCalculation() { // 模拟一个耗时的计算 return "计算结果"; } }实操心得:日志级别的选择
ERROR:系统发生了错误,必须有人立即处理。例如:数据库连接失败、核心服务调用异常。WARN:不应该发生,但暂时不影响系统运行,需要关注。例如:缓存失效回退到数据库、API调用超时但重试成功。INFO:重要的业务流程节点信息。例如:用户登录成功、订单创建完成。生产环境的标准输出级别。DEBUG:调试信息,用于开发阶段定位问题。例如:详细的SQL参数、方法入参出参。TRACE:最详细的跟踪信息,通常只在排查极端问题时开启。
5. 应用打包与部署:从JAR到Docker
开发完成的SpringBoot应用,最终需要部署到服务器上运行。SpringBoot的“打包即运行”特性让部署变得非常简单。
5.1 打包可执行JAR与瘦身
使用Maven或Gradle可以轻松打包。在项目根目录执行:
mvn clean package命令执行后,会在target目录下生成一个your-app-0.0.1-SNAPSHOT.jar文件。这个JAR是可执行的,因为它内嵌了Tomcat等Web容器(如果是Web应用)。直接运行即可:
java -jar target/your-app-0.0.1-SNAPSHOT.jarJAR包瘦身问题默认打包方式(spring-boot-maven-plugin)会生成一个“胖JAR”(Fat Jar),它包含了所有依赖库和你的应用代码。这可能导致JAR包很大(几十MB甚至上百MB)。每次更新代码,即使只改了一行,也需要上传整个大JAR包,效率低下。
解决方案:依赖分离打包可以将依赖库(lib)和你的应用代码(classes)分开打包。修改pom.xml中的插件配置:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- 启用分层打包,为Docker镜像优化做准备 --> <layers> <enabled>true</enabled> </layers> <!-- 或者,使用更传统的依赖分离 --> <!-- <executable>true</executable> <layout>ZIP</layout> --> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>更彻底的分离方式是使用maven-dependency-plugin将依赖JAR复制到target/lib目录,然后修改启动脚本的类路径。但SpringBoot的layers特性是更现代、更推荐的方式,尤其配合Docker时。
5.2 使用Docker容器化部署
Docker是目前部署应用的事实标准。它为应用提供了一个一致的运行环境。
1. 编写Dockerfile在项目根目录创建Dockerfile:
# 第一阶段:构建 # 使用包含Maven的官方镜像来构建应用 FROM maven:3.8.7-eclipse-temurin-17 AS builder WORKDIR /app # 复制pom.xml和源代码 COPY pom.xml . COPY src ./src # 构建应用,跳过测试 RUN mvn clean package -DskipTests # 第二阶段:运行 # 使用更小的JRE基础镜像,而不是完整的JDK FROM eclipse-temurin:17-jre-alpine WORKDIR /app # 从构建阶段复制构建产物 COPY --from=builder /app/target/*.jar app.jar # 创建一个非root用户运行应用,增强安全性 RUN addgroup -S spring && adduser -S spring -G spring USER spring:spring # 暴露端口(与application.yml中server.port一致) EXPOSE 8080 # 启动应用,通过环境变量传递Profile等配置 ENTRYPOINT ["java", "-jar", "/app/app.jar"]2. 构建Docker镜像在Dockerfile所在目录执行:
docker build -t my-springboot-app:latest .3. 运行容器
# 简单运行 docker run -p 8080:8080 my-springboot-app:latest # 更贴近生产的运行方式:设置环境变量、挂载日志卷、使用自定义网络 docker run -d \ --name my-app \ --network my-bridge-network \ -p 8080:8080 \ -e SPRING_PROFILES_ACTIVE=prod \ -e JAVA_OPTS="-Xms512m -Xmx1024m" \ -v /host/path/logs:/app/logs \ my-springboot-app:latest关键参数解释:
-d:后台运行。--network:将容器加入自定义网络,方便容器间通信(如连接数据库容器)。-e:设置环境变量。这里设置了激活prodProfile,并传递JVM内存参数。-v:将宿主机的目录挂载到容器内。这里将日志目录挂载出来,这样即使容器被删除,日志文件还在宿主机上。
5.3 关于“信创”与TongWeb的思考
在提供的热词中,有一个问题:“改成信创的话,是否需要东方通的tongweb”。这是一个非常实际的问题。
核心结论:通常不需要。SpringBoot应用打包成的可执行JAR,其内嵌的Web容器(默认是Tomcat)已经是一个完整的、符合Servlet规范的Web服务器。它可以直接在符合Java运行环境的信创操作系统(如麒麟、统信UOS)和信创硬件(如鲲鹏、飞腾)上运行,无需额外安装像TongWeb、金蝶Apusic、中创InforSuite这样的传统国产中间件。
传统中间件(如TongWeb)的角色,更像是一个“应用服务器”,用于部署传统的WAR包。你需要将应用打成WAR,然后部署到TongWeb的webapps目录下,由TongWeb来提供Servlet容器环境。
SpringBoot的“内嵌容器”模式,已经把这个容器(Tomcat, Jetty, Undertow)打包进了JAR里。它自己就是自己的“中间件”。因此,在信创环境下部署SpringBoot JAR包,只需要:
- 确保目标服务器安装了兼容的JRE(如龙芯JDK、毕昇JDK等信创版Java)。
- 直接运行
java -jar your-app.jar。
什么情况下会用到TongWeb?
- 如果你的项目是传统的、非SpringBoot的Java Web应用,打成了WAR包。
- 如果企业有统一的技术栈规划,强制要求所有Java应用必须部署在TongWeb上。
- 如果需要用到TongWeb提供的一些特定企业级功能,而这些功能是内嵌Tomcat没有的(不过这种情况较少,SpringBoot的生态通常能覆盖)。
所以,对于全新的SpringBoot项目,在信创环境下,优先采用内嵌容器直接运行JAR包的方式,架构更简单,运维也更方便。