Spring Boot: cómo obtener el puerto en ejecución

89

Tengo una aplicación de inicio de la primavera (usando incrustado Tomcat 7), y me he fijado server.port = 0en mi application.propertiespara que pueda tener un puerto aleatorio. Una vez que el servidor se inicia y se ejecuta en un puerto, necesito poder obtener el puerto que se eligió.

No puedo usar @Value("$server.port")porque es cero. Esta es una información aparentemente simple, entonces, ¿por qué no puedo acceder a ella desde mi código java? ¿Cómo puedo acceder a ella?

Fatigar
fuente
Relacionado: stackoverflow.com/a/24643484/1686330
Dirk Lachowski
Otra posibilidad se puede encontrar en los documentos: docs.spring.io/spring-boot/docs/current/reference/html/… (ver 64.5 Descubrir el puerto HTTP en tiempo de ejecución)
Dirk Lachowski

Respuestas:

95

¿También es posible acceder al puerto de administración de una manera similar, por ejemplo:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;
RiendoLimón
fuente
7
@LocalServerPortes solo un atajo para @Value("${local.server.port}").
demonio
@deamon significa que si no especifica local.server.port en las propiedades, no funcionará
independiente
79

Spring's Environment tiene esta información para usted.

@Autowired
Environment environment;

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

En la superficie, esto parece idéntico a inyectar un campo anotado @Value("${local.server.port}")(o @LocalServerPort, que es idéntico), mediante el cual se produce una falla de cableado automático al inicio, ya que el valor no está disponible hasta que el contexto se inicializa por completo. La diferencia aquí es que esta llamada se realiza implícitamente en la lógica empresarial en tiempo de ejecución en lugar de invocarse al inicio de la aplicación y, por lo tanto, la 'recuperación diferida' del puerto se resuelve correctamente.

hennr
fuente
4
por alguna razón, esto no funcionó para mí, lo environment.getProperty("server.port")hizo.
Anand Rockzz
24

Gracias a @Dirk Lachowski por señalarme en la dirección correcta. La solución no es tan elegante como me hubiera gustado, pero la hice funcionar. Al leer los documentos de primavera, puedo escuchar en el EmbeddedServletContainerInitializedEvent y obtener el puerto una vez que el servidor está en funcionamiento. Así es como se ve:

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();
      }
    }
Fatigar
fuente
AFAIK, esto no funcionará si está buscando configurar un bean con el puerto del servidor. Este evento no se activa hasta que se hayan cargado todos los beans y se hayan registrado los servlets.
mre
funcionó para mí en ese momento, por eso lo acepté. Sin embargo, no he probado la respuesta de Henry.
Tucker
Después de leer los documentos, se me ocurrió prácticamente la misma clase pequeña que tú, nombré PortProvidery proporcioné un getPort()método. Me conecté automáticamente PortProvideral controlador que necesitaba el puerto, y cuando mi lógica comercial llamó portProvider.getPort(), se devolvió el puerto de tiempo de ejecución.
Matthew Wise
11
Para cualquiera que intente esto con Spring Boot 2.0 o posterior, la API parece haber cambiado ligeramente. Ya no pude suscribirme EmbeddedServletContainerInitializedEvent, pero hay una clase similar llamada ServletWebServerInitializedEventque tiene un .getWebServer()método. Esto le dará al menos el puerto que está escuchando Tomcat.
NiteLite
17

Solo para que otros que han configurado sus aplicaciones como la mía se beneficien de lo que pasé ...

Ninguna de las soluciones anteriores funcionó para mí porque tengo un ./configdirectorio justo debajo de la base de mi proyecto con 2 archivos:

application.properties
application-dev.properties

En application.propertiestengo:

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

En application-dev.propertiestengo:

server_host = localhost
server_port = 8080

Esto es así cuando ejecuto mi fat jar desde la CLI, los *.propertiesarchivos se leerán desde el ./configdirectorio y todo estará bien.

Bueno, resulta que estos archivos de propiedades anulan completamente la webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTconfiguración @SpringBootTesten mis especificaciones de Spock. No importa lo que he intentado, incluso con webEnvironmentsistema de RANDOM_PORTresorte que siempre se inicie el contenedor Tomcat incluido en el puerto 8080 (o cualquier valor que había puesto en mis ./config/*.propertiesarchivos).

La ÚNICA forma en que pude superar esto fue agregando un explícito properties = "server_port=0"a la @SpringBootTestanotación en mis especificaciones de integración de Spock:

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

Entonces, y solo entonces, Spring finalmente comenzó a activar Tomcat en un puerto aleatorio. En mi humilde opinión, este es un error del marco de prueba de Spring, pero estoy seguro de que tendrán su propia opinión al respecto.

Espero que esto haya ayudado a alguien.

Jacomoman
fuente
Tiene exactamente la misma configuración y también se encontró con esto. Asumí que este era el problema en cierto sentido, pero gracias por publicar su solución aquí. ¿Sabe si alguien ha registrado esto como un error todavía?
bvulaj
15

Puede obtener el puerto que está siendo utilizado por una instancia de Tomcat incorporada durante las pruebas inyectando el valor local.server.port como tal:

// Inject which port we were assigned
@Value("${local.server.port}")
int port;
bsyk
fuente
17
local.server.portsolo se establece cuando se ejecuta con@WebIntegrationTests
ejain
WebIntegrationTest está en desuso .
kyakya
12

A partir de Spring Boot 1.4.0, puede usar esto en su prueba:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}
jabal
fuente
8

Ninguna de estas soluciones funcionó para mí. Necesitaba conocer el puerto del servidor mientras construía un bean de configuración Swagger. Usar ServerProperties funcionó para mí:

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 ejemplo usa la configuración automática Spring Boot y JAX-RS (no Spring MVC).

mre
fuente
1
Quería lo mismo para la arrogancia
Jeef
1

Después de Spring Boot 2, muchas cosas han cambiado. Las respuestas dadas anteriormente funcionan antes de Spring Boot 2. Ahora, si está ejecutando su aplicación con argumentos de tiempo de ejecución para el puerto del servidor, entonces solo obtendrá el valor estático con @Value("${server.port}"), que se menciona en el archivo application.properties . Ahora, para obtener el puerto real en el que se está ejecutando el servidor, utilice el siguiente método:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Además, si está utilizando sus aplicaciones como Eureka / Discovery Clients con carga balanceada RestTemplateo WebClient, el método anterior devolverá el número de puerto exacto.

sam
fuente
1
Esta es la respuesta correcta para Spring Boot 2. Funciona bien con @SpringBootTest y WebEnvironment.RANDOM_PORT.
Ken Pronovici
1

Puede obtener el puerto del servidor desde el

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
fuente
0

Asegúrese de haber importado el paquete correcto.

import org.springframework.core.env.Environment;

y luego usa el 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
fuente
1
He realizado cambios en mi respuesta. ¿Sigues teniendo el mismo problema?
Ahmed
0

Lo resolví con una especie de proxy bean. El cliente se inicializa cuando es necesario, para entonces el puerto debería estar disponible:

@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
fuente