Spring Boot - Cách lấy cổng đang chạy


89

Tôi có một ứng dụng khởi động mùa xuân (sử dụng tomcat 7 được nhúng) và tôi đã cài đặt ứng dụng server.port = 0của mình application.propertiesđể tôi có thể có một cổng ngẫu nhiên. Sau khi máy chủ được khởi động và chạy trên một cổng, tôi cần có thể nhận được cổng đã được chọn.

Tôi không thể sử dụng @Value("$server.port")vì nó bằng không. Đây là một thông tin có vẻ đơn giản, vậy tại sao tôi không thể truy cập nó từ mã java của mình? Làm thế nào tôi có thể truy cập nó?



Một khả năng khác có thể được tìm thấy trong tài liệu: docs.spring.io/spring-boot/docs/current/reference/html/… (xem 64.5 Khám phá cổng HTTP trong thời gian chạy)
Dirk Lachowski

Câu trả lời:


95

Cũng có thể truy cập cổng quản lý theo cách tương tự, ví dụ:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

7
@LocalServerPortchỉ là một lối tắt cho @Value("${local.server.port}").
deamon,

@deamon có nghĩa là nếu bạn không chỉ định local.server.port trong thuộc tính - nó sẽ không hoạt động
độc lập

79

Spring's Environment giữ thông tin này cho bạn.

@Autowired
Environment environment;

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

Nhìn bề ngoài, điều này trông giống với việc đưa vào một trường được chú thích @Value("${local.server.port}")(hoặc @LocalServerPortgiống hệt), theo đó lỗi tự động tạo ra khi khởi động vì giá trị không có sẵn cho đến khi khởi tạo hoàn toàn ngữ cảnh. Sự khác biệt ở đây là lệnh gọi này được thực hiện ngầm trong logic nghiệp vụ thời gian chạy thay vì được gọi khi khởi động ứng dụng, và do đó 'lazy-fetch' của cổng được giải quyết ổn.


4
vì một số lý do điều này không làm việc cho tôi, environment.getProperty("server.port")đã làm.
Anand Rockzz

24

Cảm ơn @Dirk Lachowski đã chỉ tôi đúng hướng. Giải pháp này không thanh lịch như tôi muốn, nhưng tôi đã làm được. Đọc tài liệu mùa xuân, tôi có thể nghe trên EmbeddedServletContainerInitializedEvent và nhận cổng sau khi máy chủ khởi động và chạy. Đây là những gì nó trông như thế này -

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 điều này sẽ không hoạt động nếu bạn đang tìm cách định cấu hình bean với cổng máy chủ. Sự kiện này sẽ không xảy ra cho đến khi tất cả các bean đã được tải và các servlet đã được đăng ký.
mre

nó có hiệu quả với tôi tại thời điểm đó là lý do tại sao tôi chấp nhận nó. Tôi chưa thử câu trả lời của hennr.
Tucker

Sau khi đọc tài liệu, tôi nghĩ ra hầu như cùng một lớp nhỏ giống như bạn, đặt tên cho nó PortProvidervà cung cấp một getPort()phương thức. Tự động đưa tôi PortProvidervào bộ điều khiển cần cổng và khi logic nghiệp vụ của tôi được gọi portProvider.getPort(), cổng thời gian chạy được trả về.
Matthew Wise

11
Đối với bất kỳ ai đang thử điều này với Spring Boot 2.0 trở lên, API dường như đã thay đổi một chút. Tôi không thể đăng ký được nữa EmbeddedServletContainerInitializedEvent, nhưng có một lớp tương tự được gọi là ServletWebServerInitializedEventcó một .getWebServer()phương thức. Điều này sẽ giúp bạn có ít nhất cổng mà Tomcat đang nghe.
NiteLite

17

Để những người khác đã định cấu hình ứng dụng của họ như ứng dụng của tôi được hưởng lợi từ những gì tôi đã trải qua ...

Không có giải pháp nào ở trên phù hợp với tôi vì tôi có một ./configthư mục ngay dưới cơ sở dự án của mình với 2 tệp:

application.properties
application-dev.properties

Trong đó, application.propertiestôi có:

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

Trong application-dev.propertiestôi có:

server_host = localhost
server_port = 8080

Điều này là do đó khi tôi chạy lọ béo của mình từ CLI, các *.propertiestệp sẽ được đọc từ ./configdir và tất cả đều tốt.

Chà, hóa ra các tệp thuộc tính này hoàn toàn ghi đè webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTcài đặt trong @SpringBootTestthông số kỹ thuật Spock của tôi. Bất kể tôi đã thử gì, ngay cả khi webEnvironmentđặt thành RANDOM_PORTSpring sẽ luôn khởi động vùng chứa Tomcat được nhúng trên cổng 8080 (hoặc bất kỳ giá trị nào tôi đã đặt trong ./config/*.propertiestệp của mình ).

Cách DUY NHẤT tôi có thể khắc phục điều này là thêm một chú thích rõ ràng properties = "server_port=0"vào @SpringBootTestthông số kỹ thuật tích hợp Spock của tôi:

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

Sau đó, và chỉ sau đó, Spring cuối cùng mới bắt đầu quay Tomcat trên một cổng ngẫu nhiên. IMHO đây là một lỗi khung kiểm tra Spring, nhưng tôi chắc chắn rằng họ sẽ có ý kiến ​​riêng về điều này.

Hy vọng điều này đã giúp ai đó.


Có thiết lập chính xác này và cũng gặp phải vấn đề này. Tôi cho rằng đây là vấn đề theo một nghĩa nào đó, nhưng cảm ơn bạn đã đăng giải pháp của bạn ở đây. Bạn có biết nếu ai đó đã đăng nhập này là một lỗi chưa?
bvulaj 19/03/18

15

Bạn có thể lấy cổng đang được sử dụng bởi một phiên bản Tomcat nhúng trong quá trình kiểm tra bằng cách đưa vào giá trị local.server.port như sau:

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

17
local.server.portchỉ được đặt khi chạy với@WebIntegrationTests
ejain

WebIntegrationTest không được dùng nữa.
kyakya

12

Bắt đầu với Spring Boot 1.4.0, bạn có thể sử dụng điều này trong thử nghiệm của mình:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}

8

Không có giải pháp nào trong số này hiệu quả với tôi. Tôi cần biết cổng máy chủ trong khi xây dựng bean cấu hình Swagger. Sử dụng ServerProperties phù hợp với tôi:

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         
    }
}

Ví dụ này sử dụng cấu hình tự động Spring Boot và JAX-RS (không phải Spring MVC).


1
Tôi muốn điều tương tự cho vênh vang
Jeef

1

Sau Spring Boot 2, rất nhiều thứ đã thay đổi. Các câu trả lời đưa ra ở trên hoạt động trước Spring Boot 2. Bây giờ nếu bạn đang chạy ứng dụng của mình với các đối số thời gian chạy cho cổng máy chủ, thì bạn sẽ chỉ nhận được giá trị tĩnh với @Value("${server.port}"), được đề cập trong tệp application.properties . Bây giờ để có được cổng thực tế mà máy chủ đang chạy, hãy sử dụng phương pháp sau:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Ngoài ra, nếu bạn đang sử dụng các ứng dụng của mình dưới dạng Ứng dụng khách Eureka / Discovery với tải cân bằng RestTemplatehoặc WebClient, phương pháp trên sẽ trả về số cổng chính xác.


1
Đây là câu trả lời phù hợp cho Spring Boot 2. Hoạt động tốt với @SpringBootTest và WebEnosystem.RANDOM_PORT.
Ken Pronovici

1

Bạn có thể lấy cổng máy chủ từ

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

Hãy đảm bảo rằng bạn đã nhập đúng gói

import org.springframework.core.env.Environment;

và sau đó sử dụng đối tượng Môi trường

@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
Tôi đã thực hiện các thay đổi cho câu trả lời của mình. Bạn vẫn gặp vấn đề tương tự?
Ahmed

0

Tôi đã giải quyết nó bằng một loại đậu ủy nhiệm. Máy khách được khởi tạo khi cần thiết, khi đó cổng sẽ khả dụng:

@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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.