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

@deamon означает, что если вы не укажете local.server.port в свойствах - он не будет работать
автономно

79

Среда Spring содержит для вас эту информацию.

@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 это не сработает, если вы хотите настроить bean-компонент с портом сервера. Это событие не запускается до тех пор, пока все bean-компоненты не будут загружены и сервлеты не будут зарегистрированы.
MRE

это работало для меня в то время, поэтому я принял это. Однако я не пробовал ответить hennr.
Tucker

Прочитав документы, я придумал практически такой же небольшой класс, как и вы, назвав его PortProviderи предоставив getPort()метод. Автоматически подключил меня PortProviderк контроллеру, которому нужен порт, и при вызове моей бизнес-логики portProvider.getPort()был возвращен порт времени выполнения.
Мэтью Уайз,

11
Для тех, кто пробует это с помощью Spring Boot 2.0 или новее, API, похоже, немного изменился. Я больше не мог подписаться EmbeddedServletContainerInitializedEvent, но есть похожий класс, у ServletWebServerInitializedEventкоторого есть .getWebServer()метод. Это даст вам хотя бы порт, который прослушивает Tomcat.
NiteLite

17

Точно так же другие, которые настроили свои приложения, как мое, извлекли выгоду из того, через что я прошел ...

Ни одно из вышеперечисленных решений не помогло мне, потому что у меня есть ./configкаталог прямо под моей базой проекта с двумя файлами:

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

Это так, когда я запускаю свою толстую банку из интерфейса командной строки, *.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")

Тогда и только тогда Spring, наконец, начал запускать Tomcat на случайном порту. ИМХО, это ошибка фреймворка тестирования Spring, но я уверен, что у них будет собственное мнение по этому поводу.

Надеюсь, это кому-то помогло.


У вас точно такая же настройка, и вы тоже столкнулись с этим. Я предположил, что в некотором смысле это проблема, но спасибо, что разместили здесь свое решение. Вы знаете, зарегистрировал ли кто-нибудь это как ошибку?
bvulaj

15

Вы можете получить порт, который используется встроенным экземпляром Tomcat во время тестов, введя значение local.server.port как таковое:

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

17
local.server.portустанавливается только при беге с@WebIntegrationTests
ejain


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

Ни одно из этих решений не помогло мне. Мне нужно было знать порт сервера при создании bean-компонента конфигурации 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
Я хотел того же и для чванства
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 с балансировкой нагрузки 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;

а затем используйте объект 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.