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.