You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot中配置端口被占用时如何动态查找可用端口?

Spring Boot中配置端口被占用时如何动态查找可用端口?

嘿,这个需求太实用了——开发时经常遇到8080被占,不想用随机端口的话,自动从8080开始挨个找可用端口确实方便。我来给你一步步讲怎么实现,完全贴合你的Spring Boot Starter场景:

第一步:实现端口检测工具类

先写一个简单的工具类,负责从指定起始端口开始,逐个检测端口是否可用:

import java.net.ServerSocket;

public class PortDetector {
    public static int findAvailablePort(int startPort) {
        int port = startPort;
        while (true) {
            try (ServerSocket socket = new ServerSocket(port)) {
                return port;
            } catch (Exception e) {
                // 端口被占用,尝试下一个
                port++;
            }
        }
    }
}

这个类用ServerSocket来检测端口是否能绑定,能绑定就说明可用,直接返回;不行就自增端口继续试,逻辑简单可靠。

第二步:在Spring Boot启动前动态设置端口

要让这个逻辑在Spring Boot加载配置前生效,我们可以用ApplicationListener<ApplicationEnvironmentPreparedEvent>——这个事件会在Spring Boot环境准备好但上下文还没创建时触发,刚好适合修改配置:

import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

public class SequentialPortListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    private static final int START_PORT = 8080;

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        // 先检查用户有没有手动指定端口,如果有就不用处理了
        String configuredPort = environment.getProperty("server.port");
        if (configuredPort != null && !"0".equals(configuredPort)) {
            return;
        }

        // 查找可用端口
        int availablePort = PortDetector.findAvailablePort(START_PORT);
        // 将找到的端口设置到环境中,覆盖默认配置
        Map<String, Object> portProperties = new HashMap<>();
        portProperties.put("server.port", availablePort);
        environment.getPropertySources().addFirst(new MapPropertySource("sequential-port", portProperties));
    }
}

这里加了个贴心的判断:如果用户手动指定了非0的端口,就跳过自动检测,保留用户的自定义配置。

第三步:让Starter自动加载这个监听器

作为Starter,我们需要让Spring Boot自动发现这个监听器。最简单的方式是在resources/META-INF/spring.factories里添加:

org.springframework.context.ApplicationListener=com.yourpackage.SequentialPortListener

这样你的Starter被引入后,这个监听器就会自动生效,不用使用者额外做配置。

第四步:完善你的启动日志逻辑

你之前提到的启动时打印服务器URL的逻辑,可以补全成这样:

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;

public class ServerUrlLogger implements ApplicationListener<ApplicationReadyEvent> {
    private final Environment environment;

    // 构造注入环境对象
    public ServerUrlLogger(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        String serverPort = environment.getProperty("server.port");
        String contextPath = environment.getProperty("server.servlet.context-path", "");
        String baseUrl = "http://localhost:" + serverPort + contextPath;
        System.out.println("🚀 Application started successfully! Access URL: " + baseUrl);
    }
}

然后把这个Bean注册到你的Starter配置类里:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class StarterAutoConfiguration {
    @Bean
    public ServerUrlLogger serverUrlLogger(Environment environment) {
        return new ServerUrlLogger(environment);
    }
}

同样,这个配置类也要加到spring.factories里,让Starter自动加载:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yourpackage.StarterAutoConfiguration

最后测试一下

启动你的应用,如果8080被占,它会自动尝试8081、8082…直到找到可用端口,启动后也会打印出正确的访问URL,完美解决你的需求~

内容来源于stack exchange

火山引擎 最新活动