Spring Boot - Як отримати запущений порт


89

У мене є додаток навесні завантаження (використовуючи вбудовані 7) кіт, і я поставив server.port = 0на мою , application.propertiesтак що я можу мати випадковий порт. Після завантаження сервера та запуску на порту я повинен мати змогу отримати вибраний порт.

Я не можу використовувати, @Value("$server.port")тому що це нуль. Це, здавалося б, проста інформація, то чому я не можу отримати до неї доступ з мого коду Java? Як я можу отримати до нього доступ?



Іншу можливість можна знайти в документах: docs.spring.io/spring-boot/docs/current/reference/html/… (див. 64.5 Відкрийте порт HTTP під час виконання)
Дірк Лаховскі,

Відповіді:


95

Чи можна також отримати доступ до порту управління подібним чином, наприклад:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

7
@LocalServerPort- це просто ярлик для @Value("${local.server.port}").
Діамон,

@deamon означає, що якщо ви не вказали local.server.port у властивостях - це не працює
самостійно

79

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

24

Дякую @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 це не спрацює, якщо ви хочете налаштувати компонент з портом сервера. Ця подія запускається лише після того, як усі компоненти завантажуються та сервлети реєструються.
мере

у той час це працювало для мене, тому я і прийняв це. Але я не пробував відповіді hennr.
Такер,

Прочитавши документи, я придумав практично такий самий маленький клас, як і ви, назвавши його PortProviderта надавши getPort()метод. Автоматично підключився PortProviderдо контролера, який потребує порту, і коли зателефонувала моя бізнес-логіка portProvider.getPort(), порт виконання був повернутий.
Matthew Wise

11
Для тих, хто пробує це з 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файли будуть читатися з папки, ./configі все добре.

Ну, виявляється, ці файли властивостей повністю перевизначають webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTналаштування @SpringBootTestв моїх специфікаціях Spock. Незалежно від того, що я намагався, навіть із webEnvironmentвстановленим значенням RANDOM_PORTSpring завжди запускав вбудований контейнер Tomcat через порт 8080 (або будь-яке значення, яке я встановив у своїх ./config/*.propertiesфайлах).

Тільки так , як я був в змозі подолати це шлях додавання явного properties = "server_port=0"до @SpringBootTestанотації в моїй інтеграції специфікації Spock:

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

Тоді і лише тоді Весна нарешті почала крутити Tomcat на випадковому порту. IMHO - це помилка фреймворку тестування весни, але я впевнений, що у них буде своя думка щодо цього.

Сподіваюся, це комусь допомогло.


Виконайте таку саму установку і також натрапили на це. Я припустив, що це питання в якомусь сенсі, але дякую, що розмістили тут своє рішення. Чи знаєте ви, чи хтось ще реєстрував це як помилку?
bvulaj

15

Ви можете отримати порт, який використовується вбудованим екземпляром 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. Використання 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
Я хотів те ж саме для
шахраїв

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 із збалансованим навантаженням, RestTemplateабо WebClient, описаний вище спосіб поверне точний номер порту.


1
Це правильна відповідь для Spring Boot 2. Чудово працює з @SpringBootTest та WebEnvironment.RANDOM_PORT.
Кен Проновічі,

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
Я вніс зміни у свою відповідь. Ви все ще маєте ту ж проблему?
Ахмед

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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.