1. 概述

Spring Boot 应用默认内嵌了 Web 服务器,有时候我们可能需要在运行时动态获取 HTTP 端口。

本文将介绍几种在 Spring Boot 应用中以编程方式获取 HTTP 端口的方法。

2. 准备工作

2.1. 示例应用结构

我们先创建一个简单的 Spring Boot 应用作为演示:

@SpringBootApplication
public class GetServerPortApplication {
    public static void main(String[] args) {
        SpringApplication.run(GetServerPortApplication.class, args);
    }
}

2.2. 端口配置的两种场景

通常,最简单的方式是通过 application.propertiesapplication.yml 文件来指定应用运行的端口。

例如,在 application.properties 中设置端口为 7777:

server.port=7777

当然,也可以设置为 0,让 Spring Boot 自动选择一个可用端口:

server.port=0

接下来我们将分别演示在固定端口随机端口两种场景下如何获取运行时端口。本文示例代码主要在单元测试中获取端口。

3. 获取固定端口

首先创建一个配置文件 application-fixedport.properties,内容如下:

server.port=7777

然后编写测试类:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = GetServerPortApplication.class,
  webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ActiveProfiles("fixedport")
public class GetServerFixedPortUnitTest {
    private final static int EXPECTED_PORT = 7777;
    ....
}

说明一下几个关键注解:

  • @RunWith(SpringRunner.class):整合 JUnit 和 Spring 的测试上下文
  • @SpringBootTest(... WebEnvironment.DEFINED_PORT):使用配置文件中定义的端口启动内嵌服务器
  • @ActiveProfiles("fixedport"):激活 fixedport Profile,加载对应的配置文件

3.1. 使用 @Value("${server.port}") 获取端口

由于配置文件会被加载,我们可以直接通过 @Value 注解注入端口值:

@Value("${server.port}")
private int serverPort;

@Test
public void givenFixedPortAsServerPort_whenReadServerPort_thenGetThePort() {
    assertEquals(EXPECTED_PORT, serverPort);
}

✅ 简单粗暴,适合固定端口场景。

3.2. 使用 ServerProperties 获取端口

ServerProperties 是 Spring Boot 提供的一个配置类,封装了内嵌服务器的各种属性(端口、地址等):

@Autowired
private ServerProperties serverProperties;

@Test
public void givenFixedPortAsServerPort_whenReadServerProps_thenGetThePort() {
    int port = serverProperties.getPort();
 
    assertEquals(EXPECTED_PORT, port);
}

✅ 更加面向对象,适合对配置进行统一管理。

4. 获取随机端口

创建另一个配置文件 application-randomport.properties

server.port=0

测试类如下:

....
@ActiveProfiles("randomport")
public class GetServerRandomPortUnitTest {
...
}

同样通过 Profile 激活对应配置。

前面两种方法在随机端口场景下就不灵了

@Value("${server.port}")
private int randomServerPort;

@Test
public void given0AsServerPort_whenReadServerPort_thenGet0() {
    assertEquals(0, randomServerPort);
}

@Autowired
private ServerProperties serverProperties;

@Test
public void given0AsServerPort_whenReadServerProps_thenGet0() {
    int port = serverProperties.getPort();
 
    assertEquals(0, port);
}

❌ 上面两种方式拿到的都是配置值 0,而不是实际运行时分配的端口。

4.1. 使用 ServletWebServerApplicationContext 获取端口

Spring Boot 启动内嵌服务器后,会创建一个 ServletWebServerApplicationContext 实例。我们可以通过它获取到实际运行的 WebServer 实例:

@Autowired
private ServletWebServerApplicationContext webServerAppCtxt;

@Test
public void given0AsServerPort_whenReadWebAppCtxt_thenGetThePort() {
    int port = webServerAppCtxt.getWebServer().getPort();
 
    assertTrue(port > 1023);
}

✅ 这是获取运行时真实端口的最直接方式。

4.2. 监听 ServletWebServerInitializedEvent 事件

Spring 应用在生命周期中会发布各种事件,其中当内嵌服务器启动完成后,会发布 ServletWebServerInitializedEvent 事件。

我们可以监听这个事件来获取端口:

@Service
public class ServerPortService {
    private int port;

    public int getPort() {
        return port;
    }

    @EventListener
    public void onApplicationEvent(final ServletWebServerInitializedEvent event) {
        port = event.getWebServer().getPort();
    }
}

在测试中注入该服务即可获取端口:

@Autowired
private ServerPortService serverPortService;

@Test
public void given0AsServerPort_whenReadFromListener_thenGetThePort() {
    int port = serverPortService.getPort();
 
    assertTrue(port > 1023);
}

✅ 适合需要在应用启动后做统一处理的场景。

5. 小结

在 Spring Boot 中,我们通常通过配置文件设置应用的端口,支持固定端口或随机端口两种方式。

本文总结了以下几种获取运行时端口的方法:

方法 适用场景 是否能获取随机端口
@Value("${server.port}") 固定端口
ServerProperties.getPort() 固定端口
ServletWebServerApplicationContext.getWebServer().getPort()
ServletWebServerInitializedEvent 监听

📌 建议:如果是测试或运行时需要获取真实端口,优先使用 ServletWebServerApplicationContext 或事件监听方式。

完整代码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-environment


原始标题:Get the Running Port in Spring Boot