Spring Boot - Comment obtenir le port en cours d'exécution

89

J'ai une application de démarrage à ressort (en utilisant le tomcat intégré 7), et je l'ai défini server.port = 0dans mon application.propertiespour pouvoir avoir un port aléatoire. Une fois que le serveur est démarré et fonctionne sur un port, je dois pouvoir obtenir le port qui a été choisi.

Je ne peux pas utiliser @Value("$server.port")car c'est zéro. C'est une information apparemment simple, alors pourquoi ne puis-je pas y accéder à partir de mon code java? Comment puis-je y accéder?

Tucker
la source
En relation: stackoverflow.com/a/24643484/1686330
Dirk Lachowski
Une autre possibilité peut être trouvée dans la documentation: docs.spring.io/spring-boot/docs/current/reference/html / ... (voir 64.5 Découvrir le port HTTP au moment de l'exécution)
Dirk Lachowski

Réponses:

95

Est-il également possible d'accéder au port de gestion d'une manière similaire, par exemple:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;
RireLemon
la source
7
@LocalServerPortest juste un raccourci pour @Value("${local.server.port}").
deamon le
@deamon signifie que si vous ne spécifiez pas local.server.port dans les propriétés - cela ne fonctionnera pas
autonome le
79

Spring's Environment détient ces informations pour vous.

@Autowired
Environment environment;

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

En apparence, cela semble identique à l'injection d'un champ annoté @Value("${local.server.port}")(ou @LocalServerPort, qui est identique), dans lequel un échec de câblage automatique est déclenché au démarrage car la valeur n'est pas disponible tant que le contexte n'est pas complètement initialisé. La différence ici est que cet appel est implicitement effectué dans la logique métier d'exécution plutôt qu'appelé au démarrage de l'application, et par conséquent, le «lazy-fetch» ​​du port résout ok.

hennr
la source
4
pour une raison quelconque, cela environment.getProperty("server.port")n'a pas fonctionné pour moi .
Anand Rockzz
24

Merci à @Dirk Lachowski de m'avoir orienté dans la bonne direction. La solution n'est pas aussi élégante que je l'aurais souhaité, mais je l'ai fait fonctionner. En lisant la documentation du printemps, je peux écouter sur l'Event EmbeddedServletContainerInitializedEvent et obtenir le port une fois que le serveur est opérationnel. Voici à quoi ça ressemble -

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
la source
AFAIK cela ne fonctionnera pas si vous cherchez à configurer un bean avec le port du serveur. Cet événement n'est déclenché qu'après le chargement de tous les beans et l'enregistrement des servlets.
mre
cela a fonctionné pour moi à l'époque, c'est pourquoi je l'ai accepté. Je n'ai cependant pas essayé la réponse de Hennr.
Tucker
Après avoir lu la documentation, j'ai trouvé pratiquement la même petite classe que vous, en la nommant PortProvideret en fournissant une getPort()méthode. J'ai câblé automatiquement mon entrée PortProviderau contrôleur nécessitant le port, et lorsque ma logique métier a été appelée portProvider.getPort(), le port d'exécution a été renvoyé.
Matthew Wise
11
Pour tous ceux qui essaient cela avec Spring Boot 2.0 ou version ultérieure, l'API semble avoir légèrement changé. Je n'ai plus pu m'abonner EmbeddedServletContainerInitializedEvent, mais il existe une classe similaire appelée ServletWebServerInitializedEventqui a une .getWebServer()méthode. Cela vous donnera au moins le port que Tomcat écoute.
NiteLite
17

Juste pour que d'autres qui ont configuré leurs applications comme la mienne bénéficient de ce que j'ai vécu ...

Aucune des solutions ci-dessus n'a fonctionné pour moi car j'ai un ./configrépertoire juste sous ma base de projet avec 2 fichiers:

application.properties
application-dev.properties

Dans application.propertiesj'ai:

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

Dans application-dev.propertiesj'ai:

server_host = localhost
server_port = 8080

C'est le cas lorsque j'exécute mon gros pot à partir de la CLI, les *.propertiesfichiers seront lus à partir du répertoire ./configet tout va bien.

Eh bien, il s'avère que ces fichiers de propriétés remplacent complètement le webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTparamètre @SpringBootTestde mes spécifications Spock. Peu importe ce que j'ai essayé, même avec webEnvironmentjeu àRANDOM_PORT Spring, démarrerait toujours le conteneur Tomcat intégré sur le port 8080 (ou quelle que soit la valeur que j'avais définie dans mes ./config/*.propertiesfichiers).

La SEULE façon dont j'ai pu surmonter cela a été d'ajouter une explicite properties = "server_port=0"à l' @SpringBootTestannotation dans mes spécifications d'intégration Spock:

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

Puis, et alors seulement, Spring a finalement commencé à faire tourner Tomcat sur un port aléatoire. À mon humble avis, il s'agit d'un bogue du cadre de test Spring, mais je suis sûr qu'ils auront leur propre opinion à ce sujet.

J'espère que cela a aidé quelqu'un.

Jacomoman
la source
Avoir exactement la même configuration et également rencontré cela. J'ai supposé que c'était le problème dans un certain sens, mais merci d'avoir publié votre solution ici. Savez-vous si quelqu'un a déjà signalé cela comme un bogue?
bvulaj
15

Vous pouvez obtenir le port utilisé par une instance Tomcat intégrée pendant les tests en injectant la valeur local.server.port en tant que telle:

// Inject which port we were assigned
@Value("${local.server.port}")
int port;
bsyk
la source
17
local.server.portest défini uniquement lors de l'exécution avec@WebIntegrationTests
ejain
WebIntegrationTest est obsolète.
kyakya
12

À partir de Spring Boot 1.4.0, vous pouvez l'utiliser dans votre test:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}
jabal
la source
8

Aucune de ces solutions n'a fonctionné pour moi. J'avais besoin de connaître le port du serveur lors de la construction d'un bean de configuration Swagger. L'utilisation de ServerProperties a fonctionné pour moi:

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

Cet exemple utilise la configuration automatique de Spring Boot et JAX-RS (pas Spring MVC).

mre
la source
1
Je voulais la même chose pour swagger
Jeef
1

Après Spring Boot 2, beaucoup de choses ont changé. Les réponses données ci-dessus fonctionnent avant Spring Boot 2. Maintenant, si vous exécutez votre application avec des arguments d'exécution pour le port du serveur, vous n'obtiendrez que la valeur statique avec @Value("${server.port}"), qui est mentionnée dans le fichier application.properties . Maintenant, pour obtenir le port réel sur lequel le serveur s'exécute, utilisez la méthode suivante:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

De plus, si vous utilisez vos applications en tant que clients Eureka / Discovery avec une charge équilibrée RestTemplateou WebClient, la méthode ci-dessus renverra le numéro de port exact.

sam
la source
1
C'est la bonne réponse pour Spring Boot 2. Fonctionne très bien avec @SpringBootTest et WebEnvironment.RANDOM_PORT.
Ken Pronovici
1

Vous pouvez obtenir le port du serveur depuis le

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
la source
0

Veuillez vous assurer que vous avez importé le bon package

import org.springframework.core.env.Environment;

puis utilisez l'objet Environnement

@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
la source
1
J'ai apporté des modifications à ma réponse. Avez-vous toujours le même problème?
Ahmed le
0

Je l'ai résolu avec une sorte de bean proxy. Le client est initialisé quand il est nécessaire, d'ici là, le port devrait être 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
la source