Spring Boot-如何获取运行端口


89

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

我不能使用,@Value("$server.port")因为它是零。这是一条看似简单的信息,所以为什么不能从我的Java代码访问它?我该如何访问?



另一种可能性可以在docs中找到:docs.spring.io/spring-boot/docs/current/reference/html/…(请参见64.5在运行时发现HTTP端口)
Dirk Lachowski

Answers:


95

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

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

    @LocalServerPort
    int randomServerPort;

    @LocalManagementPort
    int randomManagementPort;

7
@LocalServerPort只是的捷径@Value("${local.server.port}")
迪蒙

@deamon表示如果您未在属性中指定local.server.port,它将无法正常工作
独立运行于

79

Spring's Environment为您保存此信息。

@Autowired
Environment environment;

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

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


4
由于某种原因,这对我不起作用,environment.getProperty("server.port")确实。
阿南·洛克兹(Annand Rockzz)'18

24

感谢@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已注册后,才会触发此事件。
MRE

当时它对我有用,这就是为什么我接受了它。我还没有尝试亨纳尔的答案。
塔克2016年

阅读文档后,我想到了与您几乎相同的小类,为它命名PortProvider并提供了一种getPort()方法。将我自动连接PortProvider到需要该端口的控制器,当我的业务逻辑调用portProvider.getPort()时,返回运行时端口。
马修·怀斯

11
对于任何使用Spring Boot 2.0或更高版本尝试此操作的人,API似乎已略有变化。我不再能够订阅EmbeddedServletContainerInitializedEvent,但是有一个类似的类ServletWebServerInitializedEvent,其中有一个.getWebServer()方法。这将使您至少获得Tomcat正在侦听的端口。
NiteLite

17

就像其他配置了我的应用程序的人一样,我也从中受益匪浅...

上面的解决方案都不适合我,因为./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运行胖jar时,*.properties将从./config目录中读取文件,一切都很好。

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

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

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

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

希望这对某人有所帮助。


具有完全相同的设置,并且也遇到了这种情况。从某种意义上讲,我认为这是问题所在,但感谢您在此处发布解决方案。您知道是否有人将其记录为错误吗?
bvulaj

15

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

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

17
local.server.port仅在与@WebIntegrationTests
ejain


12

从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;

  // ...
}

8

这些解决方案都不适合我。在构造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)。


1
我要招摇同样的事情
Jeef

1

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

    @Autowired
    private ServletWebServerApplicationContext server;

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

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

另外,如果您将应用程序用作负载平衡为RestTemplate或的Eureka / Discovery Clients WebClient,则上述方法将返回确切的端口号。


1
这是Spring Boot 2的正确答案。与@SpringBootTest和WebEnvironment.RANDOM_PORT配合良好。
肯·普罗诺维奇

1

您可以从

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

请确保您已导入正确的软件包

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

1
我已经更改了答案,您是否仍然遇到相同的问题?
艾哈迈德

0

我用一种代理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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.