Spring Boot - Çalışan bağlantı noktası nasıl alınır


91

Bir bahar çizme uygulaması (7 erkek kedi gömülü kullanarak) var ve ben kurdum server.port = 0skinTenimde application.propertiesyüzden rasgele bir bağlantı noktası olabilir. Sunucu başlatıldıktan ve bir bağlantı noktasında çalıştıktan sonra, seçilen bağlantı noktasını alabilmem gerekiyor.

@Value("$server.port")Sıfır olduğu için kullanamıyorum . Bu görünüşte basit bir bilgi parçası, öyleyse neden java kodumdan ona erişemiyorum? Nasıl ulaşabilirim?



Başka bir olasılık şu belgelerde bulunabilir: docs.spring.io/spring-boot/docs/current/reference/html/… (bkz. 64.5 Çalışma zamanında HTTP bağlantı noktasını keşfedin)
Dirk Lachowski

Yanıtlar:


97

Yönetim bağlantı noktasına benzer şekilde erişmek de mümkün mü, örneğin:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

8
@LocalServerPortsadece bir kısayol @Value("${local.server.port}").
deamon

1
@deamon, özelliklerde local.server.port'u belirtmezseniz - işe yaramayacak
tek başına

82

Spring'in Ortamı bu bilgileri sizin için tutar.

@Autowired
Environment environment;

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

Yüzeyde bu , açıklamalı @Value("${local.server.port}")(veya @LocalServerPortaynı olan) bir alanın enjekte edilmesiyle aynı görünmektedir; bu sayede, bağlam tamamen başlatılana kadar değer mevcut olmadığından başlangıçta bir otomatik kablolama hatası atılır. Buradaki fark, bu çağrının uygulama başlangıcında çağrılmaktan ziyade çalışma zamanı iş mantığında örtük olarak yapılması ve bu nedenle bağlantı noktasının 'tembel getirmesinin' düzgün çözülmesidir.


4
nedense bu benim için işe yaramadı, işe environment.getProperty("server.port")yaradı.
Anand Rockzz

25

@Dirk Lachowski'ye beni doğru yöne yönlendirdiği için teşekkürler. Çözüm istediğim kadar zarif değil ama çalıştırdım. İlkbahar belgelerini okuyarak, EmbeddedServletContainerInitializedEvent'i dinleyebilir ve sunucu çalışmaya başladığında bağlantı noktasını alabilirim. İşte göründüğü gibi -

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, sunucu bağlantı noktasıyla bir fasulye yapılandırmak istiyorsanız bu işe yaramayacaktır. Bu olay, tüm çekirdekler yüklenene ve servletler kaydedilene kadar tetiklenmez.
mre

benim için çalıştı, bu yüzden kabul ettim. Hennr'ın cevabını denemedim.
Tucker

Dokümanları okuduktan sonra, sizinle hemen hemen aynı küçük sınıfı buldum, adlandırdım PortProviderve bir getPort()yöntem sağladım. Bağlantı PortProvidernoktasına ihtiyaç duyan denetleyiciye otomatik olarak bağlandım ve iş mantığım aradığında portProvider.getPort(), çalışma zamanı bağlantı noktası döndürüldü.
Matthew Wise

12
Bunu Spring Boot 2.0 veya üstü ile deneyen herkes için API biraz değişmiş gibi görünüyor. Artık abone olamadım EmbeddedServletContainerInitializedEvent, ancak yöntemi ServletWebServerInitializedEventolan benzer bir sınıf var .getWebServer(). Bu size Tomcat'in en azından dinlediği limanı getirecek.
NiteLite

17

Uygulamalarını benimki gibi yapılandıran diğer kişilerin yaşadıklarımdan yararlanmaları için ...

Yukarıdaki çözümlerin hiçbiri benim için işe yaramadı çünkü ./configproje tabanımın hemen altında 2 dosyalı bir dizinim var:

application.properties
application-dev.properties

İçinde application.properties:

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

In application-dev.propertiesI sahiptir:

server_host = localhost
server_port = 8080

Bu, şişman kavanozumu CLI'den çalıştırdığımda, *.propertiesdosyalar dizinden okunacak ./configve her şey yolunda.

Görünüşe göre bu özellikler dosyaları, Spock özelliklerimdeki webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTayarı tamamen geçersiz kılıyor @SpringBootTest. Ne denediğimin bir önemi yok, Spring'e webEnvironmentayarlanmış olsam bile RANDOM_PORT, gömülü Tomcat konteynerini her zaman 8080 portunda (veya ./config/*.propertiesdosyalarımda ayarladığım değeri) başlatırdı .

SADECE yolu bu açık bir ekleyerek oldu üstesinden başardı properties = "server_port=0"için @SpringBootTestbenim Spock entegrasyon özellikleri ek açıklamanın:

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

Daha sonra ve ancak o zaman Spring sonunda Tomcat'i rastgele bir limanda döndürmeye başladı. IMHO bu bir Bahar testi çerçeve hatasıdır, ancak bu konuda kendi fikirlerine sahip olacaklarından eminim.

Umarım bu birine yardımcı olmuştur.


Tam olarak aynı düzene sahip olun ve bununla karşılaşın. Bir anlamda sorunun bu olduğunu varsaydım, ancak çözümünüzü burada yayınladığınız için teşekkürler. Bunu henüz bir hata olarak kaydeden biri olup olmadığını biliyor musunuz?
bvulaj

16

Local.server.port değerini şu şekilde enjekte ederek, testler sırasında gömülü bir Tomcat örneği tarafından kullanılan bağlantı noktasını elde edebilirsiniz:

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

17
local.server.portyalnızca ile çalışırken ayarlanır@WebIntegrationTests
ejain

WebIntegrationTest Kullanımdan Kaldırıldı.
kyakya

12

Spring Boot 1.4.0'dan başlayarak bunu testinizde kullanabilirsiniz:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}

8

Bu çözümlerin hiçbiri benim için işe yaramadı. Bir Swagger yapılandırma çekirdeği oluştururken sunucu bağlantı noktasını bilmem gerekiyordu. ServerProperties'i kullanmak benim için çalıştı:

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

Bu örnek, Spring Boot otomatik yapılandırmasını ve JAX-RS'yi (Spring MVC'yi değil) kullanır.


1
Aynı şeyi swagger için de istedim
Jeef

1

Spring Boot 2'den sonra çok şey değişti. Yukarıda verilen cevaplar Spring Boot 2'den önce çalışır. Şimdi uygulamanızı sunucu portu için çalışma zamanı argümanları ile çalıştırıyorsanız, o zaman sadece application.properties dosyasında @Value("${server.port}")bahsedilen statik değeri alacaksınız . Şimdi sunucunun çalıştığı gerçek bağlantı noktasını almak için aşağıdaki yöntemi kullanın:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Ayrıca, uygulamalarınızı yük dengeli RestTemplateveya Eureka / Keşif İstemcileri olarak kullanıyorsanız WebClient, yukarıdaki yöntem tam bağlantı noktası numarasını döndürecektir.


1
Spring Boot 2 için doğru cevap budur. @SpringBootTest ve WebEnvironment.RANDOM_PORT ile sorunsuz çalışır.
Ken Pronovici

1

Sunucu bağlantı noktasını şuradan alabilirsiniz:

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

Lütfen doğru paketi aldığınızdan emin olun

import org.springframework.core.env.Environment;

ve sonra Ortam nesnesini kullanın

@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
Cevabımda değişiklikler yaptım, hala aynı sorunu yaşıyor musunuz?
Ahmed

0

Bir çeşit vekil fasulye ile çözdüm. İstemci, ihtiyaç duyulduğunda başlatılır, o zamana kadar bağlantı noktası kullanılabilir olmalıdır:

@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;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.