我有一个spring boot应用程序(使用嵌入式tomcat 7),并且已经设置好了server.port = 0
,application.properties
所以可以有一个随机端口。服务器启动并在端口上运行后,我需要能够获取所选择的端口。
我不能使用,@Value("$server.port")
因为它是零。这是一条看似简单的信息,所以为什么不能从我的Java代码访问它?我该如何访问?
我有一个spring boot应用程序(使用嵌入式tomcat 7),并且已经设置好了server.port = 0
,application.properties
所以可以有一个随机端口。服务器启动并在端口上运行后,我需要能够获取所选择的端口。
我不能使用,@Value("$server.port")
因为它是零。这是一条看似简单的信息,所以为什么不能从我的Java代码访问它?我该如何访问?
Answers:
是否也可以通过类似的方式访问管理端口,例如:
@SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyTest {
@LocalServerPort
int randomServerPort;
@LocalManagementPort
int randomManagementPort;
@LocalServerPort
只是的捷径@Value("${local.server.port}")
。
Spring's Environment为您保存此信息。
@Autowired
Environment environment;
String port = environment.getProperty("local.server.port");
从表面上看,这与注入带注释的字段@Value("${local.server.port}")
(或@LocalServerPort
,该字段相同)相同,从而在启动时引发自动装配失败,因为在上下文完全初始化之前该值不可用。此处的区别在于,此调用是在运行时业务逻辑中隐式进行的,而不是在应用程序启动时进行调用,因此端口的“延迟获取”可以解决。
environment.getProperty("server.port")
确实。
感谢@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();
}
}
PortProvider
并提供了一种getPort()
方法。将我自动连接PortProvider
到需要该端口的控制器,当我的业务逻辑调用portProvider.getPort()
时,返回运行时端口。
EmbeddedServletContainerInitializedEvent
,但是有一个类似的类ServletWebServerInitializedEvent
,其中有一个.getWebServer()
方法。这将使您至少获得Tomcat正在侦听的端口。
就像其他配置了我的应用程序的人一样,我也从中受益匪浅...
上面的解决方案都不适合我,因为./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_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测试框架错误,但是我敢肯定,他们对此会有自己的看法。
希望这对某人有所帮助。
这些解决方案都不适合我。在构造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)。
在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
,则上述方法将返回确切的端口号。
请确保您已导入正确的软件包
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"));
}
我用一种代理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;
}
}