Spring Boot - Come ottenere la porta in esecuzione


91

Ho un'applicazione di avvio primaverile (utilizzando tomcat 7 integrato) e l'ho impostata server.port = 0in application.propertiesmodo da poter avere una porta casuale. Dopo che il server è stato avviato e in esecuzione su una porta, è necessario essere in grado di ottenere la porta scelta.

Non posso usare @Value("$server.port")perché è zero. Questa è un'informazione apparentemente semplice, quindi perché non posso accedervi dal mio codice java? Come posso accedervi?



Un'altra possibilità può essere trovata nella documentazione: docs.spring.io/spring-boot/docs/current/reference/html/… (vedi 64.5 Discover the HTTP port at runtime)
Dirk Lachowski

Risposte:


97

È anche possibile accedere alla porta di gestione in modo simile, ad esempio:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

8
@LocalServerPortè solo una scorciatoia per @Value("${local.server.port}").
demone

1
@deamon significa che se non specifichi local.server.port nelle proprietà - non funzionerà
stand alone

82

Spring's Environment conserva queste informazioni per te.

@Autowired
Environment environment;

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

In superficie questo sembra identico all'iniezione di un campo annotato @Value("${local.server.port}")(o @LocalServerPort, che è identico), per cui viene generato un errore di autowiring all'avvio poiché il valore non è disponibile fino a quando il contesto non è completamente inizializzato. La differenza qui è che questa chiamata viene eseguita implicitamente nella logica di business runtime piuttosto che invocata all'avvio dell'applicazione, e quindi il "lazy-fetch" della porta si risolve correttamente.


4
per qualche motivo questo non ha funzionato per me, lo ha environment.getProperty("server.port")fatto.
Anand Rockzz

25

Grazie a @Dirk Lachowski per avermi indicato nella giusta direzione. La soluzione non è così elegante come avrei voluto, ma l'ho fatta funzionare. Leggendo i documenti primaverili, posso ascoltare su EmbeddedServletContainerInitializedEvent e ottenere la porta una volta che il server è attivo e funzionante. Ecco come appare:

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

Per quanto ne so, questo non funzionerà se stai cercando di configurare un bean con la porta del server. Questo evento non viene generato fino a quando tutti i bean non sono stati caricati e i servlet sono stati registrati.
mre

ha funzionato per me in quel momento, ecco perché l'ho accettato. Tuttavia, non ho provato la risposta di Hennr.
Tucker

Dopo aver letto i documenti, ho creato praticamente la tua stessa piccola classe, nominandola PortProvidere fornendo un getPort()metodo. Autowired my PortProviderin al controller che richiede la porta e quando la mia logica di business ha chiamato portProvider.getPort(), è stata restituita la porta di runtime.
Matthew Wise

12
Per chiunque provi questo con Spring Boot 2.0 o versioni successive, l'API sembra essere leggermente cambiata. Non ero più in grado di iscrivermi EmbeddedServletContainerInitializedEvent, ma esiste una classe simile chiamataServletWebServerInitializedEvent che ha un .getWebServer()metodo. Questo ti porterà almeno alla porta che Tomcat sta ascoltando.
NiteLite

17

Solo così gli altri che hanno configurato le loro app come la mia beneficiano di quello che ho passato ...

Nessuna delle soluzioni di cui sopra ha funzionato per me perché ho una ./configdirectory appena sotto la mia base di progetto con 2 file:

application.properties
application-dev.properties

In application.propertiesho:

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

In application-dev.propertiesho:

server_host = localhost
server_port = 8080

Questo è così quando eseguo il mio fat jar dalla CLI i *.propertiesfile verranno letti dalla directory ./confige tutto va bene.

Bene, risulta che questi file delle proprietà sovrascrivono completamente l' webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTimpostazione @SpringBootTestnelle mie specifiche Spock. Indipendentemente da ciò che ho provato, anche con webEnvironmentimpostato su RANDOM_PORTSpring avvierei sempre il contenitore Tomcat incorporato sulla porta 8080 (o qualunque valore avessi impostato nel mio./config/*.properties file).

Il solo modo in cui sono stato in grado di superare questo è stato con l'aggiunta di un esplicito properties = "server_port=0"alla @SpringBootTestannotazione nei mie specifiche di integrazione Spock:

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

Poi, e solo allora, Spring ha finalmente iniziato a far girare Tomcat su una porta casuale. IMHO questo è un bug del framework di test di primavera, ma sono sicuro che avranno la loro opinione su questo.

Spero che questo abbia aiutato qualcuno.


Avere la stessa identica configurazione e anche imbattersi in questo. Ho pensato che questo fosse il problema in un certo senso, ma grazie per aver pubblicato la tua soluzione qui. Sai se qualcuno l'ha già registrato come bug?
bvulaj

16

È possibile ottenere la porta utilizzata da un'istanza Tomcat incorporata durante i test inserendo il valore local.server.port come tale:

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

17
local.server.portè impostato solo quando si esegue con@WebIntegrationTests
ejain

WebIntegrationTest è deprecato.
kyakya

12

A partire da Spring Boot 1.4.0 puoi usarlo nel tuo test:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}

8

Nessuna di queste soluzioni ha funzionato per me. Avevo bisogno di conoscere la porta del server durante la costruzione di un bean di configurazione Swagger. L'utilizzo di ServerProperties ha funzionato per me:

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

Questo esempio utilizza la configurazione automatica Spring Boot e JAX-RS (non Spring MVC).


1
Volevo la stessa cosa per spavalderia
Jeef

1

Dopo Spring Boot 2, molto è cambiato. Le risposte fornite sopra funzionano prima di Spring Boot 2. Ora, se stai eseguendo la tua applicazione con argomenti di runtime per la porta del server, otterrai solo il valore statico con @Value("${server.port}"), che è menzionato nel file application.properties . Ora per ottenere la porta effettiva in cui è in esecuzione il server, utilizzare il seguente metodo:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Inoltre, se si utilizzano le applicazioni come client Eureka / Discovery con carico bilanciato RestTemplateo WebClient, il metodo sopra riportato restituirà il numero di porta esatto.


1
Questa è la risposta giusta per Spring Boot 2. Funziona bene con @SpringBootTest e WebEnvironment.RANDOM_PORT.
Ken Pronovici

1

È possibile ottenere la porta del server da

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

Assicurati di aver importato il pacchetto corretto

import org.springframework.core.env.Environment;

e quindi utilizzare l'oggetto 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
Ho apportato modifiche alla mia risposta, stai ancora riscontrando lo stesso problema?
Ahmed

0

L'ho risolto con una specie di proxy bean. Il client viene inizializzato quando è necessario, a quel punto la porta dovrebbe essere disponibile:

@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;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.