Spring Boot - Como obter a porta de execução


91

Eu tenho um aplicativo de inicialização de primavera (usando tomcat 7 incorporado) e configurei server.port = 0no meu application.propertiespara que possa ter uma porta aleatória. Depois que o servidor é inicializado e executado em uma porta, preciso conseguir a porta que foi escolhida.

Não posso usar @Value("$server.port")porque é zero. Esta é uma informação aparentemente simples, então por que não consigo acessá-la do meu código Java? Como posso acessá-lo?



Outra possibilidade pode ser encontrada nos documentos: docs.spring.io/spring-boot/docs/current/reference/html/… (consulte 64.5 Descubra a porta HTTP em tempo de execução)
Dirk Lachowski

Respostas:


97

Também é possível acessar a porta de gerenciamento de forma semelhante, por exemplo:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

8
@LocalServerPorté apenas um atalho para @Value("${local.server.port}").
deamon

1
@deamon significa que se você não especificar local.server.port nas propriedades - não funcionará
independente

82

O ambiente do Spring guarda essas informações para você.

@Autowired
Environment environment;

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

Superficialmente, isso parece idêntico a injetar um campo anotado @Value("${local.server.port}")(ou @LocalServerPort, que é idêntico), por meio do qual uma falha de autowiring é lançada na inicialização, pois o valor não está disponível até que o contexto seja totalmente inicializado. A diferença aqui é que essa chamada está sendo feita implicitamente na lógica de negócios do tempo de execução, em vez de ser invocada na inicialização do aplicativo e, portanto, o 'lazy-fetch' da porta resolve ok.


4
por alguma razão isso não funcionou para mim, environment.getProperty("server.port")funcionou.
Anand Rockzz

25

Obrigado a @Dirk Lachowski por me apontar na direção certa. A solução não é tão elegante quanto eu gostaria, mas fiz funcionar. Lendo os documentos de primavera, posso ouvir no EmbeddedServletContainerInitializedEvent e obter a porta assim que o servidor estiver instalado e funcionando. Aqui está o que parece -

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, isso não funcionará se você estiver tentando configurar um bean com a porta do servidor. Este evento não é disparado até que todos os beans sejam carregados e os servlets registrados.
mre

funcionou para mim na época, é por isso que eu aceitei. Eu não tentei a resposta de hennr embora.
Tucker

Depois de ler os documentos, cheguei praticamente à mesma classe pequena que você, nomeando-a PortProvidere fornecendo um getPort()método. Autowired my PortProviderin para o controlador que precisa da porta, e quando minha lógica de negócios chamou portProvider.getPort(), a porta runtime foi retornada.
Matthew Wise

12
Para quem está tentando fazer isso com Spring Boot 2.0 ou posterior, a API parece ter mudado um pouco. Não consegui mais me inscrever EmbeddedServletContainerInitializedEvent, mas existe uma classe semelhante chamada ServletWebServerInitializedEventque possui um .getWebServer()método. Isso lhe dará a porta que o Tomcat está escutando.
NiteLite de

17

Apenas para que outras pessoas que configuraram seus aplicativos como o meu se beneficiem do que eu passei ...

Nenhuma das soluções acima funcionou para mim porque tenho um ./configdiretório logo abaixo da base do meu projeto com 2 arquivos:

application.properties
application-dev.properties

Em application.propertieseu tenho:

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

Em application-dev.propertieseu tenho:

server_host = localhost
server_port = 8080

Isto é, quando eu executo meu jar de gordura a partir da CLI, os *.propertiesarquivos serão lidos do ./configdiretório e está tudo bem.

Bem, acontece que esses arquivos de propriedades substituem completamente a webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTconfiguração @SpringBootTestem minhas especificações de Spock. Não importa o que eu tentei, mesmo com webEnvironmentdefinido como RANDOM_PORTSpring, sempre inicializaria o contêiner Tomcat integrado na porta 8080 (ou qualquer valor que eu definisse em meus ./config/*.propertiesarquivos).

A ÚNICA maneira de superar isso foi adicionando um texto explícito properties = "server_port=0"à @SpringBootTestanotação em minhas especificações de integração do Spock:

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

Então, e só então, o Spring finalmente começou a girar o Tomcat em uma porta aleatória. IMHO, este é um bug do framework de testes do Spring, mas tenho certeza que eles terão suas próprias opiniões sobre isso.

Espero que isso tenha ajudado alguém.


Tenha exatamente a mesma configuração e também corri para isso. Presumi que esse era o problema de alguma forma, mas obrigado por postar sua solução aqui. Você sabe se alguém já registrou isso como um bug?
bvulaj

16

Você pode obter a porta que está sendo usada por uma instância incorporada do Tomcat durante os testes, injetando o valor local.server.port como tal:

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

17
local.server.portsó é definido quando executado com@WebIntegrationTests
ejain

WebIntegrationTest está obsoleto.
Kyakya,

12

A partir do Spring Boot 1.4.0, você pode usar isso em seu teste:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}

8

Nenhuma dessas soluções funcionou para mim. Eu precisava saber a porta do servidor ao construir um bean de configuração Swagger. Usar ServerProperties funcionou para mim:

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

Este exemplo usa a configuração automática Spring Boot e JAX-RS (não Spring MVC).


1
Eu queria a mesma coisa para arrogância
Jeef

1

Depois do Spring Boot 2, muita coisa mudou. As respostas fornecidas acima funcionam antes do Spring Boot 2. Agora, se você estiver executando seu aplicativo com argumentos de tempo de execução para a porta do servidor, obterá apenas o valor estático com @Value("${server.port}"), que é mencionado no arquivo application.properties . Agora, para obter a porta real na qual o servidor está sendo executado, use o seguinte método:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Além disso, se você estiver usando seus aplicativos como clientes Eureka / Discovery com balanceamento de carga RestTemplateou WebClient, o método acima retornará o número de porta exato.


1
Esta é a resposta certa para Spring Boot 2. Funciona bem com @SpringBootTest e WebEnvironment.RANDOM_PORT.
Ken Pronovici

1

Você pode obter a porta do servidor do

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

Certifique-se de que importou o pacote correto

import org.springframework.core.env.Environment;

e então usar o objeto 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
Eu fiz alterações em minha resposta. Você ainda está tendo o mesmo problema?
Ahmed

0

Eu resolvi isso com uma espécie de bean proxy. O cliente é inicializado quando necessário, então a porta deve estar disponível:

@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;
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.