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?

Tucker
fonte
Relacionado: stackoverflow.com/a/24643484/1686330
Dirk Lachowski
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;
LaughingLemon
fonte
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.

hennr
fonte
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();
      }
    }
Tucker
fonte
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.

Jacomoman
fonte
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;
bsyk
fonte
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;

  // ...
}
Jabal
fonte
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).

mre
fonte
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.

sam
fonte
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();
}
    
mafei
fonte
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"));
    }
Ahmed
fonte
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;
    }
}
Janning
fonte