Spring Boot - So erhalten Sie den laufenden Port

89

Ich habe eine Spring-Boot-Anwendung (mit Embedded Tomcat 7) und habe server.port = 0meine application.propertiesso eingestellt, dass ich einen zufälligen Port haben kann. Nachdem der Server gestartet wurde und auf einem Port ausgeführt wird, muss ich in der Lage sein, den ausgewählten Port abzurufen.

Ich kann nicht verwenden, @Value("$server.port")weil es Null ist. Dies ist eine scheinbar einfache Information. Warum kann ich nicht über meinen Java-Code darauf zugreifen? Wie kann ich darauf zugreifen?

Tucker
quelle
Siehe auch
Dirk Lachowski
Eine andere Möglichkeit finden Sie in den Dokumenten: docs.spring.io/spring-boot/docs/current/reference/html/… (siehe 64.5 Ermitteln des HTTP-Ports zur Laufzeit)
Dirk Lachowski

Antworten:

95

Ist es auch möglich, auf ähnliche Weise auf den Verwaltungsport zuzugreifen, z.

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;
LaughingLemon
quelle
6
@LocalServerPortist nur eine Abkürzung für @Value("${local.server.port}").
Deamon
@deamon bedeutet, wenn Sie local.server.port nicht in den Eigenschaften angeben - es funktioniert nicht
stehen Sie
78

Spring's Environment hält diese Informationen für Sie bereit.

@Autowired
Environment environment;

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

Auf den ersten Blick sieht dies identisch aus mit dem Einfügen eines mit Anmerkungen versehenen @Value("${local.server.port}")(oder @LocalServerPortidentischen) Felds, bei dem beim Start ein Autowire-Fehler ausgelöst wird, da der Wert erst verfügbar ist, wenn der Kontext vollständig initialisiert ist. Der Unterschied besteht darin, dass dieser Aufruf implizit in der Laufzeit-Geschäftslogik erfolgt und nicht beim Start der Anwendung aufgerufen wird. Daher wird das "Lazy-Fetch" des Ports in Ordnung aufgelöst.

hennr
quelle
4
Aus irgendeinem Grund hat das bei mir nicht environment.getProperty("server.port")funktioniert.
Anand Rockzz
24

Vielen Dank an @Dirk Lachowski, der mich in die richtige Richtung gelenkt hat. Die Lösung ist nicht so elegant, wie ich es mir gewünscht hätte, aber ich habe sie zum Laufen gebracht. Wenn ich die Frühlingsdokumente lese, kann ich das EmbeddedServletContainerInitializedEvent abhören und den Port abrufen, sobald der Server betriebsbereit ist. So sieht es aus -

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
quelle
AFAIK Dies funktioniert nicht, wenn Sie eine Bean mit dem Server-Port konfigurieren möchten. Dieses Ereignis wird erst ausgelöst, nachdem alle Beans geladen und die Servlets registriert wurden.
mre
es hat bei mir damals funktioniert, deshalb habe ich es akzeptiert. Ich habe die Antwort von hennr allerdings nicht ausprobiert.
Tucker
Nachdem ich die Dokumente gelesen hatte, fand ich praktisch dieselbe kleine Klasse wie Sie, benannte sie PortProviderund stellte eine getPort()Methode bereit. Ich habe mich automatisch PortProvidermit dem Controller verbunden, der den Port benötigt, und als meine Geschäftslogik aufgerufen wurde portProvider.getPort(), wurde der Laufzeitport zurückgegeben.
Matthew Wise
10
Für alle, die dies mit Spring Boot 2.0 oder höher versuchen, scheint sich die API geringfügig geändert zu haben. Ich konnte mich nicht mehr anmelden EmbeddedServletContainerInitializedEvent, aber es gibt eine ähnliche Klasse ServletWebServerInitializedEventmit einer .getWebServer()Methode. Dadurch erhalten Sie zumindest den Port, den Tomcat abhört.
NiteLite
17

Nur damit andere, die ihre Apps wie meine konfiguriert haben, von dem profitieren, was ich durchgemacht habe ...

Keine der oben genannten Lösungen hat für mich funktioniert, da ich ein ./configVerzeichnis direkt unter meiner Projektbasis mit 2 Dateien habe:

application.properties
application-dev.properties

In application.propertiesIch habe:

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

In habe application-dev.propertiesich:

server_host = localhost
server_port = 8080

Dies ist so, wenn ich mein Fat Jar über die CLI starte, werden die *.propertiesDateien aus dem ./configVerzeichnis gelesen und alles ist gut.

Nun, es stellt sich heraus, dass diese Eigenschaftendateien die webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTEinstellung @SpringBootTestin meinen Spock-Spezifikationen vollständig überschreiben . Egal, was ich versucht habe, selbst wenn Spring webEnvironmenteingestellt ist, wird RANDOM_PORTder eingebettete Tomcat-Container auf Port 8080 immer gestartet (oder welcher Wert auch immer ich in meinen ./config/*.propertiesDateien festgelegt habe).

Die einzige Möglichkeit, dies zu überwinden, bestand darin properties = "server_port=0", der @SpringBootTestAnmerkung in meinen Spock-Integrationsspezifikationen eine explizite Anmerkung hinzuzufügen :

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

Dann und erst dann fing Spring endlich an, Tomcat an einem zufälligen Port hochzufahren. IMHO ist dies ein Spring Testing Framework Bug, aber ich bin sicher, dass sie ihre eigene Meinung dazu haben werden.

Hoffe das hat jemandem geholfen.

Jacomoman
quelle
Habe genau das gleiche Setup und bin auch darauf gestoßen. Ich nahm an, dass dies in gewissem Sinne das Problem war, aber danke, dass Sie Ihre Lösung hier veröffentlicht haben. Wissen Sie, ob jemand dies bereits als Fehler angemeldet hat?
Bvulaj
15

Sie können den Port, der von einer eingebetteten Tomcat-Instanz während der Tests verwendet wird, abrufen, indem Sie den Wert local.server.port als solchen einfügen:

// Inject which port we were assigned
@Value("${local.server.port}")
int port;
bsyk
quelle
17
local.server.portwird nur eingestellt, wenn mit@WebIntegrationTests
ejain
WebIntegrationTest ist veraltet.
Kyakya
12

Ab Spring Boot 1.4.0 können Sie dies in Ihrem Test verwenden:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}
Jabal
quelle
8

Keine dieser Lösungen hat bei mir funktioniert. Ich musste den Server-Port kennen, während ich eine Swagger-Konfigurations-Bean erstellte. Die Verwendung von ServerProperties hat bei mir funktioniert:

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

In diesem Beispiel werden die automatische Konfiguration von Spring Boot und JAX-RS (nicht Spring MVC) verwendet.

mre
quelle
1
Ich wollte das gleiche für
Prahlerei
1

Nach Spring Boot 2 hat sich viel geändert. Die oben angegebenen Antworten funktionieren vor Spring Boot 2. Wenn Sie Ihre Anwendung nun mit Laufzeitargumenten für den Server-Port ausführen, erhalten Sie nur den statischen Wert mit @Value("${server.port}"), der in der Datei application.properties angegeben ist. Verwenden Sie nun die folgende Methode, um den tatsächlichen Port abzurufen, auf dem der Server ausgeführt wird:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Wenn Sie Ihre Anwendungen als Eureka / Discovery-Clients mit Lastenausgleich RestTemplateoder verwenden WebClient, gibt die oben beschriebene Methode die genaue Portnummer zurück.

Sam
quelle
1
Dies ist die richtige Antwort für Spring Boot 2. Funktioniert problemlos mit @SpringBootTest und WebEnvironment.RANDOM_PORT.
Ken Pronovici
1

Sie können den Server-Port von der erhalten

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

Bitte stellen Sie sicher, dass Sie das richtige Paket importiert haben

import org.springframework.core.env.Environment;

und verwenden Sie dann das Umgebungsobjekt

@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
quelle
1
Ich habe Änderungen an meiner Antwort vorgenommen. Haben Sie immer noch das gleiche Problem?
Ahmed
0

Ich habe es mit einer Art Proxy-Bean gelöst. Der Client wird bei Bedarf initialisiert. Bis dahin sollte der Port verfügbar sein:

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