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?



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;

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.


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();
      }
    }

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.


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;

17
local.server.portsolo se establece cuando se ejecuta con@WebIntegrationTests
ejain


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;

  // ...
}

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).


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.


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();
}
    

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"));
    }

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;
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.