Spring Boot - Cara mendapatkan port yang sedang berjalan

91

Saya memiliki aplikasi boot musim semi (menggunakan tomcat 7 tertanam), dan saya telah mengatur server.port = 0di saya application.propertiessehingga saya dapat memiliki port acak. Setelah server di-boot dan berjalan di port, saya harus bisa mendapatkan port yang dipilih.

Saya tidak bisa menggunakan @Value("$server.port")karena nol. Ini adalah informasi yang tampaknya sederhana, jadi mengapa saya tidak dapat mengaksesnya dari kode java saya? Bagaimana cara mengaksesnya?

Makanan
sumber
Terkait: stackoverflow.com/a/24643484/1686330
Dirk Lachowski
Kemungkinan lain dapat ditemukan di dokumen: docs.spring.io/spring-boot/docs/current/reference/html/… (lihat 64.5 Temukan port HTTP saat runtime)
Dirk Lachowski

Jawaban:

97

Apakah mungkin juga untuk mengakses port manajemen dengan cara yang serupa, misalnya:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;
LaughingLemon
sumber
8
@LocalServerPorthanyalah jalan pintas untuk @Value("${local.server.port}").
deamon
1
@deamon berarti jika Anda tidak menentukan local.server.port di properti - tidak akan berfungsi
berdiri sendiri
82

Lingkungan Spring menyimpan informasi ini untuk Anda.

@Autowired
Environment environment;

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

Di permukaan, ini terlihat identik dengan memasukkan bidang yang dianotasi @Value("${local.server.port}")(atau @LocalServerPort, yang identik), di mana kegagalan pemasangan kabel otomatis dilemparkan saat permulaan karena nilainya tidak tersedia hingga konteksnya sepenuhnya diinisialisasi. Perbedaannya di sini adalah bahwa panggilan ini secara implisit dibuat dalam logika bisnis waktu proses daripada dipanggil saat aplikasi dimulai, dan karenanya 'pengambilan lambat' port diselesaikan dengan baik.

hennr
sumber
4
untuk beberapa alasan ini tidak berhasil untuk saya, environment.getProperty("server.port")lakukan.
Anand Rockzz
25

Terima kasih kepada @Dirk Lachowski karena telah mengarahkan saya ke arah yang benar. Solusinya tidak seelegan yang saya inginkan, tetapi saya berhasil. Membaca dokumen musim semi, saya dapat mendengarkan di EmbeddedServletContainerInitializedEvent dan mendapatkan port setelah server aktif dan berjalan. Ini tampilannya -

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();
      }
    }
Makanan
sumber
AFAIK ini tidak akan berfungsi jika Anda ingin mengkonfigurasi kacang dengan port server. Acara ini tidak dijalankan sampai semua kacang telah dimuat dan servlet telah terdaftar.
mre
itu berhasil untuk saya pada saat itulah mengapa saya menerimanya. Saya belum mencoba jawaban hennr sekalipun.
Tucker
Setelah membaca dokumen, saya mendapatkan kelas kecil yang hampir sama dengan Anda, menamainya PortProvider, dan memberikan getPort()metode. Autowired saya PortProviderke pengontrol yang membutuhkan port, dan ketika logika bisnis saya dipanggil portProvider.getPort(), port runtime dikembalikan.
Matthew Wise
12
Bagi siapa pun yang mencoba ini dengan Spring Boot 2.0 atau yang lebih baru, API tampaknya sedikit berubah. Saya tidak lagi dapat berlangganan EmbeddedServletContainerInitializedEvent, tetapi ada kelas serupa yang disebut ServletWebServerInitializedEventyang memiliki .getWebServer()metode. Ini akan memberi Anda port yang didengarkan Tomcat.
NiteLite
17

Begitu juga orang lain yang telah mengonfigurasi aplikasi mereka seperti milik saya mendapat manfaat dari apa yang saya alami ...

Tak satu pun dari solusi di atas berhasil untuk saya karena saya memiliki ./configdirektori tepat di bawah basis proyek saya dengan 2 file:

application.properties
application-dev.properties

Di application.propertiessaya punya:

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

Di application-dev.propertiessaya punya:

server_host = localhost
server_port = 8080

Ini jadi ketika saya menjalankan tabung lemak saya dari CLI *.propertiesfile akan dibaca dari ./configdir dan semuanya baik-baik saja.

Nah, ternyata file properti ini sepenuhnya menggantikan webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORTpengaturan di @SpringBootTestspesifikasi Spock saya. Tidak peduli apa yang saya coba, bahkan dengan webEnvironmentset ke RANDOM_PORTSpring akan selalu memulai wadah Tomcat yang disematkan pada port 8080 (atau nilai apa pun yang saya tetapkan di ./config/*.propertiesfile saya ).

SATU - SATUNYA cara saya dapat mengatasi ini adalah dengan menambahkan eksplisit properties = "server_port=0"ke @SpringBootTestanotasi dalam spesifikasi integrasi Spock saya:

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

Kemudian, dan baru kemudian Spring akhirnya mulai memutar Tomcat di port acak. IMHO ini adalah bug kerangka pengujian Musim Semi, tapi saya yakin mereka akan memiliki pendapat sendiri tentang ini.

Semoga ini membantu seseorang.

Jacomoman
sumber
Miliki pengaturan yang sama persis dan juga mengalami ini. Saya berasumsi ini adalah masalahnya dalam beberapa hal, tetapi terima kasih telah memposting solusi Anda di sini. Apakah Anda tahu jika ada yang telah mencatat ini sebagai bug?
bvulaj
16

Anda bisa mendapatkan port yang digunakan oleh instance Tomcat yang disematkan selama pengujian dengan memasukkan nilai local.server.port seperti:

// Inject which port we were assigned
@Value("${local.server.port}")
int port;
bsyk
sumber
17
local.server.porthanya disetel saat dijalankan dengan@WebIntegrationTests
ejain
WebIntegrationTest Tidak Berlaku Lagi.
kyakya
12

Dimulai dengan Spring Boot 1.4.0 Anda dapat menggunakan ini dalam pengujian Anda:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}
jabal
sumber
8

Tak satu pun dari solusi ini berhasil untuk saya. Saya perlu mengetahui port server saat membuat kacang konfigurasi Swagger. Menggunakan ServerProperties berhasil untuk saya:

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

Contoh ini menggunakan konfigurasi otomatis Spring Boot dan JAX-RS (bukan Spring MVC).

mre
sumber
1
Saya menginginkan hal yang sama untuk kesombongan
ampun
1

Setelah Spring Boot 2, banyak hal yang berubah. Jawaban yang diberikan di atas berfungsi sebelum Spring Boot 2. Sekarang jika Anda menjalankan aplikasi dengan argumen runtime untuk port server, maka Anda hanya akan mendapatkan nilai statis dengan @Value("${server.port}"), yang disebutkan dalam file application.properties . Sekarang untuk mendapatkan port aktual tempat server berjalan, gunakan metode berikut:

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

Juga, jika Anda menggunakan aplikasi Anda sebagai Klien Eureka / Discovery dengan beban seimbang RestTemplateatau WebClient, metode di atas akan mengembalikan nomor port yang tepat.

sam
sumber
1
Ini adalah jawaban yang tepat untuk Spring Boot 2. Bekerja dengan baik dengan @SpringBootTest dan WebEnvironment.RANDOM_PORT.
Ken Pronovici
1

Anda bisa mendapatkan port server dari

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

Harap pastikan Anda telah mengimpor paket yang benar

import org.springframework.core.env.Environment;

dan kemudian gunakan objek Lingkungan

@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
sumber
1
Saya telah membuat perubahan pada jawaban saya, apakah Anda masih mengalami masalah yang sama?
Ahmed
0

Saya menyelesaikannya dengan semacam kacang proxy. Klien diinisialisasi saat dibutuhkan, pada saat itu port harus tersedia:

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