Spring Boot-如何获得正在运行的端口

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

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

140542 次浏览

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

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

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

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

@Autowired
Environment environment;


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

从表面上看,这与注入一个带注释的字段 @Value("${local.server.port}")(或 @LocalServerPort,这是相同的)是一样的,在启动时抛出一个自动布线故障,因为该值在上下文完全初始化之前是不可用的。这里的区别在于,这个调用是在运行时业务逻辑中隐式进行的,而不是在应用程序启动时调用的,因此端口的“延迟获取”解决了问题。

这些方法对我都不管用。在构建一个 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 auto 配置和 JAX-RS (而不是 Spring MVC)。

Starting with Spring Boot 1.4.0 you can use this in your test:

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


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


@LocalServerPort
int randomPort;


// ...
}

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

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


@LocalServerPort
int randomServerPort;


@LocalManagementPort
int randomManagementPort;

只是为了让其他像我一样配置应用程序的人从我的经历中受益..。

None of the above solutions worked for me because I have a ./config directory just under my project base with 2 files:

application.properties
application-dev.properties < br > < br > 在 application.properties我有:

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

In application-dev.properties I have:

server_host = localhost
server_port = 8080

这样,当我从 CLI 运行我的胖罐子时,*.properties文件将从 ./config目录中读取,一切正常。

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

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

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

Then, and only then did Spring finally start to spin up Tomcat on a random port. IMHO this is a Spring testing framework bug, but I'm sure they'll have their own opinion on this.

希望这对谁有帮助。

请确保您已导入了正确的包

import org.springframework.core.env.Environment;

然后使用 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"));
}

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

    @Autowired
private ServletWebServerApplicationContext server;


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


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

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

You can get the server port from the

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

我用一种代理 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;
}
}

我在 Spring2.5.5中使用 Junit4.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);
}


}