亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文詳解SpringBoot如何同時(shí)監(jiān)聽多個(gè)端口

 更新時(shí)間:2025年10月21日 08:19:29   作者:風(fēng)象南  
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何同時(shí)監(jiān)聽多個(gè)端口,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下

前言

在日常開發(fā)中,我們通常構(gòu)建的 Spring Boot 應(yīng)用都是"單面"的——一個(gè)端口,一套服務(wù)邏輯。但在某些實(shí)際場(chǎng)景中,我們可能需要一個(gè)應(yīng)用能夠"一心二用":同時(shí)提供兩套完全不同的服務(wù),分別在不同的端口上運(yùn)行。

比如:

  • 一個(gè)端口面向外部用戶,提供 API 服務(wù)
  • 另一個(gè)端口面向內(nèi)部管理,提供監(jiān)控和運(yùn)維功能
  • 或者在一個(gè)應(yīng)用中同時(shí)集成管理后臺(tái)和用戶前臺(tái)

場(chǎng)景示例

假設(shè)我們要開發(fā)一個(gè)電商平臺(tái),需要同時(shí)滿足:

用戶端服務(wù)(端口8082)

  • 商品瀏覽
  • 購(gòu)物車管理
  • 訂單處理

管理端服務(wù)(端口8083)

  • 商品管理
  • 訂單管理
  • 數(shù)據(jù)統(tǒng)計(jì)

這兩套服務(wù)功能完全不同,但需要部署在同一個(gè)應(yīng)用中。

技術(shù)實(shí)現(xiàn)方案

方案一:多 Tomcat Connector 配置

最直接的方式是配置多個(gè) Tomcat Connector。

1. 創(chuàng)建基礎(chǔ)項(xiàng)目結(jié)構(gòu)

// 主應(yīng)用類
@SpringBootApplication
public class DualPortApplication {
    public static void main(String[] args) {
        SpringApplication.run(DualPortApplication.class, args);
    }
}

2. 配置雙端口

@Configuration
public class DualPortConfiguration {

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

        // 添加第一個(gè)連接器(用戶端)
        factory.addAdditionalTomcatConnectors(createUserPortConnector());
        // 添加第二個(gè)連接器(管理端)
        factory.addAdditionalTomcatConnectors(createAdminPortConnector());

        return factory;
    }

    private Connector createUserPortConnector() {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(8080);
        connector.setProperty("connectionTimeout", "20000");
        return connector;
    }

    private Connector createAdminPortConnector() {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(8081);
        connector.setProperty("connectionTimeout", "20000");
        return connector;
    }
}

3. 路由分離策略

現(xiàn)在我們需要為不同端口提供不同的路由處理:

@Component
public class PortBasedFilter implements Filter {

    private static final String USER_PORT_HEADER = "X-User-Port";
    private static final String ADMIN_PORT_HEADER = "X-Admin-Port";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        int port = httpRequest.getLocalPort();

        if (port == 8082) {
            // 用戶端請(qǐng)求
            httpRequest.setAttribute("serviceType", "USER");
        } else if (port == 8083) {
            // 管理端請(qǐng)求
            httpRequest.setAttribute("serviceType", "ADMIN");
        }

        chain.doFilter(request, response);
    }
}

4. 創(chuàng)建分離的 Controller

// 用戶端 Controller
@RestController
@RequestMapping("/api/user")
public class UserController {

    @GetMapping("/products")
    public String getProducts() {
        return "User Products API";
    }

    @PostMapping("/cart")
    public String addToCart() {
        return "Add to cart";
    }
}

// 管理端 Controller
@RestController
@RequestMapping("/api/admin")
public class AdminController {

    @GetMapping("/products")
    public String manageProducts() {
        return "Admin Products Management";
    }

    @GetMapping("/statistics")
    public String getStatistics() {
        return "Admin Statistics";
    }
}

方案二:基于路徑前綴的更優(yōu)雅方案

上述方案雖然可行,但在實(shí)際使用中可能會(huì)有一些問題。讓我們采用更優(yōu)雅的方案。

1. 自定義 Web MVC 配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 為用戶端配置前綴
        configurer.addPathPrefix("/user", cls -> cls.isAnnotationPresent(UserApi.class));
        // 為管理端配置前綴
        configurer.addPathPrefix("/admin", cls -> cls.isAnnotationPresent(AdminApi.class));
    }
}

// 定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserApi {}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminApi {}

2. 使用注解標(biāo)記 Controller

@RestController
@RequestMapping("/products")
@UserApi
public class UserProductController {

    @GetMapping
    public String getProducts() {
        return "用戶端商品列表";
    }

    @GetMapping("/{id}")
    public String getProduct(@PathVariable String id) {
        return "商品詳情: " + id;
    }
}

@RestController
@RequestMapping("/products")
@AdminApi
public class AdminProductController {

    @GetMapping
    public String getAllProducts() {
        return "管理端商品管理列表";
    }

    @PostMapping
    public String createProduct() {
        return "創(chuàng)建商品";
    }

    @PutMapping("/{id}")
    public String updateProduct(@PathVariable String id) {
        return "更新商品: " + id;
    }
}

高級(jí)特性實(shí)現(xiàn)

1. 端口感知的攔截器

@Component
public class PortAwareInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler) throws Exception {
        int port = request.getLocalPort();

        if (port == 8082) {
            // 用戶端邏輯
            validateUserRequest(request);
        } else if (port == 8083) {
            // 管理端邏輯
            validateAdminRequest(request);
        }

        return true;
    }

    private void validateUserRequest(HttpServletRequest request) {
        // 用戶端請(qǐng)求驗(yàn)證邏輯
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null) {
            throw new SecurityException("Invalid user request");
        }
    }

    private void validateAdminRequest(HttpServletRequest request) {
        // 管理端請(qǐng)求驗(yàn)證邏輯
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new SecurityException("Admin authentication required");
        }
    }
}

2. 端口特定的異常處理

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(
            Exception e, HttpServletRequest request) {

        int port = request.getLocalPort();
        ErrorResponse error = new ErrorResponse();

        if (port == 8082) {
            error.setCode("USER_ERROR_" + e.hashCode());
            error.setMessage("用戶服務(wù)異常: " + e.getMessage());
        } else if (port == 8083) {
            error.setCode("ADMIN_ERROR_" + e.hashCode());
            error.setMessage("管理服務(wù)異常: " + e.getMessage());
        }

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(error);
    }
}

3. 動(dòng)態(tài)端口配置

@Configuration
@ConfigurationProperties(prefix = "dual.port")
@Data
public class DualPortProperties {
    private int userPort = 8082;
    private int adminPort = 8083;

    @Bean
    public ServletWebServerFactory servletContainer(DualPortProperties properties) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

        factory.addAdditionalTomcatConnectors(
            createConnector("user", properties.getUserPort()));
        factory.addAdditionalTomcatConnectors(
            createConnector("admin", properties.getAdminPort()));

        return factory;
    }

    private Connector createConnector(String name, int port) {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(port);
        connector.setName(name + "-connector");
        return connector;
    }
}

監(jiān)控和日志

1. 分端口日志記錄

@Configuration
public class LoggingConfiguration {

    @Bean
    public Logger userLogger() {
        return LoggerFactory.getLogger("USER-PORT");
    }

    @Bean
    public Logger adminLogger() {
        return LoggerFactory.getLogger("ADMIN-PORT");
    }
}

@Component
public class PortAwareLogger {

    private final Logger userLogger;
    private final Logger adminLogger;

    public PortAwareLogger(Logger userLogger, Logger adminLogger) {
        this.userLogger = userLogger;
        this.adminLogger = adminLogger;
    }

    public void logRequest(HttpServletRequest request) {
        int port = request.getLocalPort();
        String uri = request.getRequestURI();
        String method = request.getMethod();

        if (port == 8082) {
            userLogger.info("用戶端請(qǐng)求: {} {}", method, uri);
        } else if (port == 8083) {
            adminLogger.info("管理端請(qǐng)求: {} {}", method, uri);
        }
    }
}

2. 端口特定的健康檢查

@Component
public class DualPortHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        return Health.up()
                .withDetail("user-port", 8082)
                .withDetail("admin-port", 8083)
                .withDetail("status", "Both ports are active")
                .build();
    }
}

@RestController
@RequestMapping("/health")
public class HealthController {

    @GetMapping("/user")
    public Map<String, Object> userHealth() {
        Map<String, Object> health = new HashMap<>();
        health.put("port", 8082);
        health.put("status", "UP");
        health.put("service", "user-api");
        return health;
    }

    @GetMapping("/admin")
    public Map<String, Object> adminHealth() {
        Map<String, Object> health = new HashMap<>();
        health.put("port", 8083);
        health.put("status", "UP");
        health.put("service", "admin-api");
        return health;
    }
}

安全考慮

端口訪問控制

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers(req -> req.getLocalPort() == 8082)
                    .permitAll()
                .requestMatchers(req -> req.getLocalPort() == 8083)
                    .hasRole("ADMIN")
                .anyRequest().denyAll()
            )
            .formLogin(form -> form
                .loginPage("/admin/login")
                .permitAll()
            );

        return http.build();
    }
}

總結(jié)

構(gòu)建"雙面" Spring Boot 應(yīng)用是一個(gè)有趣且實(shí)用的技術(shù)挑戰(zhàn)。通過本文介紹的多種實(shí)現(xiàn)方案,我們可以根據(jù)實(shí)際需求選擇最適合的方式:

多 Connector 方案:適合簡(jiǎn)單場(chǎng)景,實(shí)現(xiàn)直接

路徑前綴方案:適合需要清晰 API 結(jié)構(gòu)的場(chǎng)景

在某些特定場(chǎng)景下確實(shí)能夠簡(jiǎn)化系統(tǒng)架構(gòu),降低運(yùn)維成本。但同時(shí)也要注意避免過度復(fù)雜化,確保系統(tǒng)的可維護(hù)性和可擴(kuò)展性。

到此這篇關(guān)于一文詳解SpringBoot如何同時(shí)監(jiān)聽多個(gè)端口的文章就介紹到這了,更多相關(guān)SpringBoot監(jiān)聽多個(gè)端口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • maven混淆打包的實(shí)現(xiàn)步驟

    maven混淆打包的實(shí)現(xiàn)步驟

    本文主要介紹了maven混淆打包的實(shí)現(xiàn)步驟,包含了Maven項(xiàng)目混淆、瘦身、打包exe這幾個(gè)方面,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • SpringBoot使用Mybatis-Plus中分頁(yè)插件PaginationInterceptor詳解

    SpringBoot使用Mybatis-Plus中分頁(yè)插件PaginationInterceptor詳解

    文章介紹SpringBoot高版本中使用MyBatisPlusInterceptor替代舊分頁(yè)插件,需配置多個(gè)InnerInterceptor功能模塊(如分頁(yè)、多租戶、動(dòng)態(tài)表名等),并強(qiáng)調(diào)插件順序和mapper.xml中SQL語句不能以分號(hào)結(jié)尾,以避免分頁(yè)語法錯(cuò)誤
    2025-07-07
  • SpringBoot文件上傳大小設(shè)置方式(yml中配置)

    SpringBoot文件上傳大小設(shè)置方式(yml中配置)

    這篇文章主要介紹了SpringBoot文件上傳大小設(shè)置方式(yml中配置),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 解決spring boot 配置文件后綴的一個(gè)坑

    解決spring boot 配置文件后綴的一個(gè)坑

    這篇文章主要介紹了spring boot 配置文件后綴的一個(gè)坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java中的CopyOnWriteArrayList詳解

    Java中的CopyOnWriteArrayList詳解

    這篇文章主要介紹了Java中的CopyOnWriteArrayList詳解,ArrayList單線程下是安全的 但是多線程下存在不安全的問題,多線程下是不安全的,需要的朋友可以參考下
    2023-12-12
  • Java為何需要平衡方法調(diào)用與內(nèi)聯(lián)

    Java為何需要平衡方法調(diào)用與內(nèi)聯(lián)

    這篇文章主要介紹了Java為何需要平衡方法調(diào)用與內(nèi)聯(lián),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-01-01
  • list集合去除重復(fù)對(duì)象的實(shí)現(xiàn)

    list集合去除重復(fù)對(duì)象的實(shí)現(xiàn)

    下面小編就為大家?guī)硪黄猯ist集合去除重復(fù)對(duì)象的實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-01-01
  • Java過濾器Filter的基本使用教程

    Java過濾器Filter的基本使用教程

    過濾器通常對(duì)一些web資源進(jìn)行攔截,做完一些處理器再交給下一個(gè)過濾器處理,直到所有的過濾器處理器,再調(diào)用servlet實(shí)例的service方法進(jìn)行處理。本文將通過示例為大家講解Java中過濾器Filter的用法與實(shí)現(xiàn),需要的可以參考一下
    2023-02-02
  • 關(guān)于HashSet與HashMap的區(qū)別及說明

    關(guān)于HashSet與HashMap的區(qū)別及說明

    這篇文章主要介紹了關(guān)于HashSet與HashMap的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 解密Spring?Boot深入理解條件裝配與條件注解

    解密Spring?Boot深入理解條件裝配與條件注解

    條件注解是一種特殊的注解,用于標(biāo)記在配置類、組件類或方法上,它們根據(jù)某些條件的結(jié)果來決定是否應(yīng)用相應(yīng)的配置或組件,這篇文章主要介紹了解密Spring?Boot深入理解條件裝配與條件注解,需要的朋友可以參考下
    2024-06-06

最新評(píng)論