Mam aplikację do rozruchu sprężynowego (przy użyciu osadzonego tomcat 7) i ustawiłem server.port = 0
w moim, application.properties
więc mogę mieć losowy port. Po uruchomieniu serwera i uruchomieniu go na porcie muszę mieć możliwość uzyskania portu, który został wybrany.
Nie mogę użyć, @Value("$server.port")
ponieważ jest zero. To pozornie prosta informacja, więc dlaczego nie mogę uzyskać do niej dostępu z mojego kodu java? Jak mogę uzyskać do niego dostęp?
spring
spring-boot
port
embedded-tomcat-7
Wielkie żarcie
źródło
źródło
Odpowiedzi:
Czy można również uzyskać dostęp do portu zarządzania w podobny sposób, np .:
@SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.RANDOM_PORT) public class MyTest { @LocalServerPort int randomServerPort; @LocalManagementPort int randomManagementPort;
źródło
@LocalServerPort
to tylko skrót do@Value("${local.server.port}")
.Spring's Environment przechowuje te informacje dla Ciebie.
@Autowired Environment environment; String port = environment.getProperty("local.server.port");
Z pozoru wygląda to tak samo, jak wstawianie pola z adnotacjami
@Value("${local.server.port}")
(lub@LocalServerPort
, które jest identyczne), w wyniku czego podczas uruchamiania zgłaszany jest błąd automatycznego okablowania, ponieważ wartość nie jest dostępna do momentu pełnej inicjalizacji kontekstu. Różnica polega na tym, że to wywołanie jest niejawnie wykonywane w logice biznesowej środowiska wykonawczego, a nie jest wywoływane podczas uruchamiania aplikacji, a zatem „leniwe pobieranie” portu rozwiązuje się prawidłowo.źródło
environment.getProperty("server.port")
.Dziękuję @Dirk Lachowski za wskazanie mi właściwego kierunku. Rozwiązanie nie jest tak eleganckie, jak bym sobie życzył, ale udało mi się. Czytając dokumentację Spring, mogę nasłuchiwać w EmbeddedServletContainerInitializedEvent i uzyskać port, gdy serwer jest już uruchomiony. Oto, jak to wygląda -
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(); } }
źródło
PortProvider
i podającgetPort()
metodę. Automatycznie połączyłemPortProvider
się z kontrolerem wymagającym portu, a gdy zadzwoniła logika biznesowaportProvider.getPort()
, zwrócony został port środowiska wykonawczego.EmbeddedServletContainerInitializedEvent
, ale istnieje podobna klasa o nazwie,ServletWebServerInitializedEvent
która ma.getWebServer()
metodę. Pozwoli ci to przynajmniej uzyskać port, którego słucha Tomcat.Tylko po to, aby inni, którzy skonfigurowali swoje aplikacje takie jak moja, skorzystali z tego, przez co przeszedłem ...
Żadne z powyższych rozwiązań nie zadziałało, ponieważ mam
./config
katalog tuż pod bazą projektu z 2 plikami:application.properties
application-dev.properties
W
application.properties
mam:spring.profiles.active = dev # set my default profile to 'dev'
W
application-dev.properties
mam:server_host = localhost server_port = 8080
Dzieje się tak, gdy uruchomię mój fat jar z CLI,
*.properties
pliki zostaną odczytane z katalogu./config
i wszystko jest w porządku.Cóż, okazuje się, że te pliki właściwości całkowicie zastępują
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
ustawienie@SpringBootTest
w moich specyfikacjach Spocka. Bez względu na to, co próbowałem, nawet przywebEnvironment
ustawieniu naRANDOM_PORT
Spring zawsze uruchamiałby osadzony kontener Tomcat na porcie 8080 (lub jakąkolwiek wartość, którą ustawiłem w moich./config/*.properties
plikach).TYLKO sposób udało mi się przezwyciężyć ten był wyraźny dodając
properties = "server_port=0"
do@SpringBootTest
adnotacji w moich specyfikacji integracyjnych Spock:@SpringBootTest (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server_port=0")
Wtedy i dopiero wtedy Spring wreszcie zaczęła uruchamiać Tomcata na losowym porcie. IMHO to jest błąd w ramach testów wiosennych, ale jestem pewien, że będą mieli na ten temat własne zdanie.
Mam nadzieję, że to komuś pomogło.
źródło
Możesz pobrać port używany przez osadzoną instancję Tomcat podczas testów, wstrzykując wartość local.server.port jako taką:
// Inject which port we were assigned @Value("${local.server.port}") int port;
źródło
local.server.port
jest ustawiana tylko podczas pracy z@WebIntegrationTests
Począwszy od Spring Boot 1.4.0, możesz użyć tego w swoim teście:
import org.springframework.boot.context.embedded.LocalServerPort; @SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.RANDOM_PORT) public class MyTest { @LocalServerPort int randomPort; // ... }
źródło
Żadne z tych rozwiązań nie działało dla mnie. Musiałem znać port serwera podczas tworzenia komponentu bean konfiguracji Swaggera. Korzystanie z ServerProperties zadziałało:
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 } }
W tym przykładzie zastosowano automatyczną konfigurację Spring Boot i JAX-RS (nie Spring MVC).
źródło
Po Spring Boot 2 wiele się zmieniło. Powyższe odpowiedzi działają przed Spring Boot 2. Teraz, jeśli uruchamiasz aplikację z argumentami runtime dla portu serwera, otrzymasz tylko wartość statyczną
@Value("${server.port}")
, która jest wymieniona w pliku application.properties . Teraz, aby uzyskać rzeczywisty port, na którym działa serwer, użyj następującej metody:@Autowired private ServletWebServerApplicationContext server; @GetMapping("/server-port") public String serverPort() { return "" + server.getWebServer().getPort(); }
Ponadto, jeśli używasz aplikacji jako klientów Eureka / Discovery ze zrównoważonym obciążeniem
RestTemplate
lubWebClient
powyższa metoda zwróci dokładny numer portu.źródło
Możesz uzyskać port serwera z
@Autowired private HttpServletRequest request; @GetMapping(value = "/port") public Object getServerPort() { System.out.println("I am from " + request.getServerPort()); return "I am from " + request.getServerPort(); }
źródło
Upewnij się, że zaimportowałeś właściwy pakiet
import org.springframework.core.env.Environment;
a następnie użyj obiektu 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")); }
źródło
Rozwiązałem to za pomocą czegoś w rodzaju fasoli zastępczej. Klient jest inicjowany, gdy jest potrzebny, do tego czasu port powinien być dostępny:
@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; } }
źródło