Spring Boot - วิธีรับพอร์ตทำงาน


91

ฉันมีแอปพลิเคชั่นสปริงบูต (โดยใช้ tomcat 7 ในตัว) และฉันได้ตั้งค่าไว้server.port = 0ในของฉันapplication.propertiesเพื่อให้ฉันมีพอร์ตแบบสุ่ม หลังจากเซิร์ฟเวอร์บูตและทำงานบนพอร์ตฉันต้องสามารถรับพอร์ตที่เลือกได้

ฉันไม่สามารถใช้ได้@Value("$server.port")เพราะมันเป็นศูนย์ นี่เป็นข้อมูลที่ดูเหมือนเรียบง่ายเหตุใดฉันจึงไม่สามารถเข้าถึงได้จากรหัส java ของฉัน ฉันจะเข้าถึงได้อย่างไร?


ที่เกี่ยวข้อง: stackoverflow.com/a/24643484/1686330
Dirk Lachowski

ความเป็นไปได้อื่น ๆ สามารถพบได้ในเอกสาร: docs.spring.io/spring-boot/docs/current/reference/html/… (ดู 64.5 ค้นพบพอร์ต HTTP ที่รันไทม์)
Dirk Lachowski

คำตอบ:


97

เป็นไปได้ไหมที่จะเข้าถึงพอร์ตการจัดการในลักษณะเดียวกันเช่น:

  @SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.RANDOM_PORT)
  public class MyTest {

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

8
@LocalServerPort@Value("${local.server.port}")เป็นเพียงทางลัดสำหรับ
deamon

1
@deamon หมายถึงถ้าคุณไม่ระบุ local.server.port ในคุณสมบัติ - มันจะไม่ทำงาน
โดดเดียว

82

Spring's Environment เก็บข้อมูลนี้ไว้ให้คุณ

@Autowired
Environment environment;

String port = environment.getProperty("local.server.port");

บนพื้นผิวลักษณะนี้จะเหมือนกับการแทรกฟิลด์ที่มีคำอธิบายประกอบ@Value("${local.server.port}")(หรือ@LocalServerPortซึ่งเหมือนกัน) โดยที่ความล้มเหลวในการป้อนอัตโนมัติจะเกิดขึ้นเมื่อเริ่มต้นเนื่องจากค่าจะไม่พร้อมใช้งานจนกว่าบริบทจะเริ่มต้นอย่างสมบูรณ์ ความแตกต่างที่นี่คือการเรียกนี้โดยปริยายถูกสร้างขึ้นในตรรกะทางธุรกิจรันไทม์แทนที่จะเรียกใช้เมื่อเริ่มต้นแอปพลิเคชันและด้วยเหตุนี้ 'การดึงข้อมูลแบบขี้เกียจ' ของพอร์ตจึงแก้ปัญหาได้


4
ด้วยเหตุผลบางอย่างสิ่งนี้ไม่ได้ผลสำหรับฉันenvironment.getProperty("server.port")ทำ
Anand Rockzz

25

ขอบคุณ @Dirk Lachowski ที่ชี้ให้ฉันไปในทิศทางที่ถูกต้อง วิธีแก้ปัญหาไม่สวยหรูอย่างที่ฉันชอบ แต่ฉันก็ใช้งานได้ การอ่านเอกสารฤดูใบไม้ผลิฉันสามารถฟังบน EmbeddedServletContainerInitializedEvent และรับพอร์ตเมื่อเซิร์ฟเวอร์เริ่มทำงาน นี่คือสิ่งที่ดูเหมือน -

import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;




    @Component
    public class MyListener implements ApplicationListener<EmbeddedServletContainerInitializedEvent> {

      @Override
      public void onApplicationEvent(final EmbeddedServletContainerInitializedEvent event) {
          int thePort = event.getEmbeddedServletContainer().getPort();
      }
    }

AFAIK สิ่งนี้จะไม่ทำงานหากคุณต้องการกำหนดค่า bean ด้วยพอร์ตเซิร์ฟเวอร์ เหตุการณ์นี้จะไม่เริ่มทำงานจนกว่าจะโหลดถั่วทั้งหมดและลงทะเบียน servlets แล้ว
MRE

มันได้ผลสำหรับฉันในเวลานั้นนั่นคือเหตุผลที่ฉันยอมรับมัน ฉันยังไม่ได้ลองคำตอบของ hennr
Tucker

หลังจากอ่านเอกสารแล้วฉันก็พบกับคลาสเล็ก ๆ แบบเดียวกับคุณตั้งชื่อPortProviderและระบุgetPort()วิธีการ ป้อนข้อมูลอัตโนมัติของฉันPortProviderไปยังคอนโทรลเลอร์ที่ต้องการพอร์ตและเมื่อตรรกะทางธุรกิจของฉันเรียกportProvider.getPort()ใช้พอร์ตรันไทม์จะถูกส่งคืน
Matthew Wise

12
สำหรับใครที่ลองใช้ Spring Boot 2.0 หรือใหม่กว่าดูเหมือนว่า API จะมีการเปลี่ยนแปลงเล็กน้อย ฉันไม่สามารถสมัครสมาชิกได้อีกต่อไปEmbeddedServletContainerInitializedEventแต่มีคลาสที่คล้ายกันที่เรียกว่าServletWebServerInitializedEventซึ่งมี.getWebServer()วิธีการ สิ่งนี้จะทำให้คุณได้รับพอร์ตที่ Tomcat กำลังฟังอย่างน้อย
NiteLite

17

คนอื่น ๆ ที่กำหนดค่าแอปของพวกเขาเช่นฉันจะได้รับประโยชน์จากสิ่งที่ฉันทำ ...

วิธีแก้ปัญหาข้างต้นไม่ได้ผลสำหรับฉันเพราะฉันมี./configไดเร็กทอรีใต้ฐานโปรเจ็กต์ที่มี 2 ไฟล์:

application.properties
application-dev.properties

ในapplication.propertiesฉันมี:

spring.profiles.active = dev  # set my default profile to 'dev'

ในapplication-dev.propertiesฉันมี:

server_host = localhost
server_port = 8080

นี่คือเมื่อฉันเรียกใช้โถไขมันของฉันจาก CLI *.propertiesไฟล์จะถูกอ่านจาก./configdir และทั้งหมดนั้นดี

ปรากฎว่าไฟล์คุณสมบัติเหล่านี้ลบล้างการwebEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTตั้งค่าใน @SpringBootTestข้อกำหนด Spock ของฉันอย่างสมบูรณ์ ไม่ว่าฉันจะพยายามอะไรก็ตามแม้ว่าการwebEnvironmentตั้งค่าเป็นRANDOM_PORTSpring ก็จะเริ่มต้นคอนเทนเนอร์ Tomcat แบบฝังในพอร์ต 8080 (หรือค่าใดก็ตามที่ฉันตั้งไว้ใน./config/*.propertiesไฟล์ของฉัน)

เฉพาะทางที่ฉันก็สามารถที่จะเอาชนะนี้คือโดยการเพิ่มอย่างชัดเจนproperties = "server_port=0"กับ@SpringBootTestคำอธิบายประกอบในรายละเอียดสป็อคบูรณาการของฉัน:

@SpringBootTest (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server_port=0")

จากนั้นในที่สุด Spring ก็เริ่มหมุน Tomcat บนพอร์ตแบบสุ่ม IMHO นี่เป็นข้อผิดพลาดของ Spring testing framework แต่ฉันแน่ใจว่าพวกเขาจะมีความคิดเห็นของตัวเองเกี่ยวกับเรื่องนี้

หวังว่านี่จะช่วยใครสักคน


มีการตั้งค่าเดียวกันนี้และพบกับสิ่งนี้ด้วย ฉันคิดว่านี่เป็นปัญหาในแง่หนึ่ง แต่ขอขอบคุณที่โพสต์วิธีแก้ปัญหาของคุณที่นี่ คุณรู้หรือไม่ว่ามีใครบันทึกสิ่งนี้เป็นบั๊กหรือยัง?
bvulaj

16

คุณสามารถรับพอร์ตที่อินสแตนซ์ Tomcat ถูกใช้งานระหว่างการทดสอบได้โดยการฉีดค่า local.server.port ดังต่อไปนี้

// Inject which port we were assigned
@Value("${local.server.port}")
int port;

17
local.server.portถูกตั้งค่าเมื่อทำงานด้วย@WebIntegrationTests
ejain

WebIntegrationTestเลิกใช้งาน
kyakya

12

เริ่มต้นด้วย Spring Boot 1.4.0 คุณสามารถใช้สิ่งนี้ในการทดสอบของคุณ:

import org.springframework.boot.context.embedded.LocalServerPort;

@SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyTest {

  @LocalServerPort
  int randomPort;

  // ...
}

8

วิธีแก้ปัญหาเหล่านี้ไม่ได้ผลสำหรับฉัน ฉันจำเป็นต้องรู้พอร์ตเซิร์ฟเวอร์ในขณะที่สร้าง Swagger configuration bean การใช้ServerPropertiesทำงานให้ฉัน:

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.ws.rs.ApplicationPath;

import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;

import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

@Component
@ApplicationPath("api")
public class JerseyConfig extends ResourceConfig 
{
    @Inject
    private org.springframework.boot.autoconfigure.web.ServerProperties serverProperties;

    public JerseyConfig() 
    {
        property(org.glassfish.jersey.server.ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
    }

    @PostConstruct
    protected void postConstruct()
    {
        // register application endpoints
        registerAndConfigureSwaggerUi();
    }

    private void registerAndConfigureSwaggerUi()
    {
        register(ApiListingResource.class);
        register(SwaggerSerializers.class);

        final BeanConfig config = new BeanConfig();
        // set other properties
        config.setHost("localhost:" + serverProperties.getPort()); // gets server.port from application.properties file         
    }
}

ตัวอย่างนี้ใช้การกำหนดค่าอัตโนมัติ Spring Boot และ JAX-RS (ไม่ใช่ Spring MVC)


1
ฉันต้องการสิ่งเดียวกันสำหรับผยอง
Jeef

1

หลังจาก Spring Boot 2 มีการเปลี่ยนแปลงมากมาย คำตอบที่ระบุข้างต้นใช้ได้ผลก่อน Spring Boot 2 ตอนนี้หากคุณกำลังเรียกใช้แอปพลิเคชันของคุณด้วยอาร์กิวเมนต์รันไทม์สำหรับพอร์ตเซิร์ฟเวอร์คุณจะได้รับเฉพาะค่าคง@Value("${server.port}")ที่ซึ่งระบุไว้ในไฟล์application.properties ตอนนี้เพื่อรับพอร์ตจริงที่เซิร์ฟเวอร์กำลังทำงานให้ใช้วิธีการต่อไปนี้:

    @Autowired
    private ServletWebServerApplicationContext server;

    @GetMapping("/server-port")
    public String serverPort() {

        return "" + server.getWebServer().getPort();
    }

นอกจากนี้หากคุณใช้แอปพลิเคชันของคุณเป็น Eureka / Discovery Clients ที่มีโหลดบาลานซ์RestTemplateหรือWebClientวิธีการข้างต้นจะส่งคืนหมายเลขพอร์ตที่แน่นอน


1
นี่คือคำตอบที่ถูกต้องสำหรับ Spring Boot 2 ทำงานได้ดีกับ @SpringBootTest และ WebEnvironment.RANDOM_PORT
Ken Pronovici

1

คุณสามารถรับพอร์ตเซิร์ฟเวอร์จากไฟล์

HttpServletRequest
@Autowired
private HttpServletRequest request;

@GetMapping(value = "/port")
public Object getServerPort() {
   System.out.println("I am from " + request.getServerPort());
   return "I am from  " + request.getServerPort();
}
    

0

โปรดตรวจสอบว่าคุณได้นำเข้าบรรจุภัณฑ์ที่ถูกต้อง

import org.springframework.core.env.Environment;

จากนั้นใช้วัตถุสภาพแวดล้อม

@Autowired
private Environment env;    // Environment Object containts the port number

 @GetMapping("/status")
  public String status()
    {
   return "it is runing on"+(env.getProperty("local.server.port"));
    }

1
ฉันได้ทำการเปลี่ยนแปลงคำตอบของฉันคุณยังคงมีปัญหาเดิมอยู่หรือไม่?
Ahmed

0

ฉันแก้ไขด้วยพร็อกซีบีนชนิดหนึ่ง ไคลเอนต์ได้รับการเตรียมใช้งานเมื่อจำเป็นจากนั้นพอร์ตควรพร้อมใช้งาน:

@Component
public class GraphQLClient {

    private ApolloClient apolloClient;
    private final Environment environment;

    public GraphQLClient(Environment environment) {
        this.environment = environment;
    }

    public ApolloClient getApolloClient() {
        if (apolloClient == null) {
            String port = environment.getProperty("local.server.port");
            initApolloClient(port);
        }
        return apolloClient;
    }

    public synchronized void initApolloClient(String port) {
        this.apolloClient = ApolloClient.builder()
                .serverUrl("http://localhost:" + port + "/graphql")
                .build();
    }

    public <D extends Operation.Data, T, V extends Operation.Variables> GraphQLCallback<T> graphql(Operation<D, T, V> operation) {
        GraphQLCallback<T> graphQLCallback = new GraphQLCallback<>();
        if (operation instanceof Query) {
            Query<D, T, V> query = (Query<D, T, V>) operation;
            getApolloClient()
                    .query(query)
                    .enqueue(graphQLCallback);
        } else {
            Mutation<D, T, V> mutation = (Mutation<D, T, V>) operation;
            getApolloClient()
                    .mutate(mutation)
                    .enqueue(graphQLCallback);

        }
        return graphQLCallback;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.