【问题标题】:Spring Boot - How to get the running portSpring Boot - 如何获取正在运行的端口
【发布时间】:2015-07-30 11:31:44
【问题描述】:

我有一个 spring boot 应用程序(使用嵌入式 tomcat 7),我在我的application.properties 中设置了server.port = 0,所以我可以有一个随机端口。在服务器启动并在端口上运行后,我需要能够获取选择的端口。

我不能使用@Value("$server.port"),因为它为零。这是一条看似简单的信息,为什么我不能从我的 java 代码中访问它呢?我怎样才能访问它?

【问题讨论】:

标签: spring spring-boot port embedded-tomcat-7


【解决方案1】:

是否也可以通过类似的方式访问管理端口,例如:

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

【讨论】:

  • @LocalServerPort 只是@Value("${local.server.port}") 的快捷方式。
  • @deamon 表示如果你没有在属性中指定 local.server.port - 它不会工作
  • 使用webEnvironment = WebEnvironment.RANDOM_PORT 解决了这个问题。谢谢
【解决方案2】:

Spring 的环境为您保存这些信息。

@Autowired
Environment environment;

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

从表面上看,这与注入注释为@Value("${local.server.port}")(或@LocalServerPort,相同)的字段相同,因此在启动时会引发自动装配失败,因为在上下文完全初始化之前该值不可用。这里的不同之处在于,这个调用是在运行时业务逻辑中隐式进行的,而不是在应用程序启动时调用,因此端口的“延迟获取”可以解决。

【讨论】:

  • 由于某种原因这对我不起作用,environment.getProperty("server.port") 做到了。
【解决方案3】:

感谢@Dirk Lachowski 为我指明了正确的方向。该解决方案并不像我希望的那样优雅,但我得到了它的工作。阅读 spring 文档,我可以监听 EmbeddedServletContainerInitializedEvent 并在服务器启动并运行后获取端口。这是它的样子 -

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 如果您要使用服务器端口配置 bean,这将不起作用。在加载所有 bean 并注册 servlet 之前,不会触发此事件。
  • 当时它对我有用,这就是我接受它的原因。不过,我还没有尝试过hennr 的答案。
  • 阅读文档后,我想出了与您几乎相同的小类,将其命名为PortProvider,并提供getPort() 方法。将我的PortProvider 自动连接到需要端口的控制器中,当我的业务逻辑调用portProvider.getPort() 时,返回运行时端口。
  • 对于尝试使用 Spring Boot 2.0 或更高版本的任何人,API 似乎略有变化。我不再能够订阅EmbeddedServletContainerInitializedEvent,但是有一个类似的类叫做ServletWebServerInitializedEvent,它有一个.getWebServer() 方法。这将为您提供 Tomcat 至少正在侦听的端口。
【解决方案4】:

您可以在测试期间通过注入 local.server.port 值来获取嵌入式 Tomcat 实例正在使用的端口:

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

【讨论】:

  • local.server.port 仅在使用@WebIntegrationTests 运行时设置
  • WebIntegrationTest 已弃用。
【解决方案5】:

让其他像我一样配置了他们的应用程序的人从我经历的事情中受益......

上述解决方案均不适合我,因为我的项目库下有一个 ./config 目录,其中包含 2 个文件:

application.properties
application-dev.properties

application.properties 我有:

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

application-dev.properties 我有:

server_host = localhost
server_port = 8080

所以当我从 CLI 运行我的 fat jar 时,*.properties 文件将从 ./config 目录中读取,一切都很好。

好吧,事实证明这些属性文件完全覆盖了我的 Spock 规范中 @SpringBootTest 中的 webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT 设置。无论我尝试什么,即使将webEnvironment 设置为RANDOM_PORT,Spring 总是会在端口 8080 上启动嵌入式 Tomcat 容器(或者我在./config/*.properties 文件中设置的任何值)。

我能够克服这个问题的唯一方法是在我的 Spock 集成规范中将显式 properties = "server_port=0" 添加到 @SpringBootTest 注释:

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

然后,直到那时,Spring 才最终开始在随机端口上启动 Tomcat。恕我直言,这是一个 Spring 测试框架错误,但我相信他们对此会有自己的看法。

希望这对某人有所帮助。

【讨论】:

  • 有这个完全相同的设置,也遇到了这个。我认为这是某种意义上的问题,但感谢您在此处发布您的解决方案。你知道是否有人将此记录为错误?
【解决方案6】:

从 Spring Boot 1.4.0 开始,您可以在测试中使用它:

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

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

  @LocalServerPort
  int randomPort;

  // ...
}

【讨论】:

    【解决方案7】:

    这些解决方案都不适合我。在构建 Swagger 配置 bean 时,我需要知道服务器端口。使用 ServerProperties 对我有用:

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

    此示例使用 Spring Boot 自动配置和 JAX-RS(不是 Spring MVC)。

    【讨论】:

    • 我想要同样的东西来招摇
    【解决方案8】:

    在 Spring Boot 2 之后,发生了很多变化。上面给出的答案在 Spring Boot 2 之前有效。现在,如果您使用服务器端口的运行时参数运行应用程序,那么您将只能获得带有 @Value("${server.port}") 的静态值,这在 application.properties 中提到 文件。现在要获取服务器运行的实际端口,请使用以下方法:

        @Autowired
        private ServletWebServerApplicationContext server;
    
        @GetMapping("/server-port")
        public String serverPort() {
    
            return "" + server.getWebServer().getPort();
        }
    

    此外,如果您将应用程序用作负载平衡 RestTemplateWebClient 的 Eureka/Discovery 客户端,则上述方法将返回确切的端口号。

    【讨论】:

    • 这是 Spring Boot 2 的正确答案。适用于 @SpringBootTest 和 WebEnvironment.RANDOM_PORT。
    【解决方案9】:

    您可以从服务器端口获取

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

    【讨论】:

    • 我认为这是个坏主意。可以在发出请求时检索信息。可能有人想在发出第一个请求之前知道启动时的端口。
    • 另一个如果调整不当可能会很严重的问题是 HttpServletRequest 被设置为控制器类的私有成员变量。当在“同一”时间有两个请求时,“请求”的设置将被覆盖,因为该类是一个单例(不是吗? - 让我知道)如果它是线程方式的,那么实现就可以了。 (另见:stackoverflow.com/a/4506382/845117
    【解决方案10】:

    我在 Spring 2.5.5 并使用 Junit 4.13.2,这是我的解决方案:

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.web.server.LocalServerPort;
    import org.springframework.test.context.junit4.SpringRunner;
    
    // tell Java the environment your testcase running is in Spring, 
    // which will enable the auto configuration such as value injection
    @RunWith(SpringRunner.class)
    @SpringBootTest(
        class = Application.class, 
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class SimpleWebTest {
    
        @LocalServerPort
        private int randomPort;
    
        @Test
        public void test() {
            // use randomPort ...
            System.out.println(randomPort);
        }
    
    }
    

    【讨论】:

      【解决方案11】:

      请确保您已导入正确的包

      import org.springframework.core.env.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"));
          }
      

      【讨论】:

      • 我已经对我的答案进行了更改,您还有同样的问题吗?
      【解决方案12】:

      我用一种代理 bean 解决了这个问题。客户端在需要时被初始化,此时端口应该可用:

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

      【讨论】:

        猜你喜欢
        • 2018-08-09
        • 2019-01-08
        • 2019-04-30
        • 2021-01-29
        • 2016-08-02
        • 2019-03-20
        • 2015-09-02
        • 2021-03-27
        • 2016-02-15
        相关资源
        最近更新 更多