假设我有以下控制器:
@RestController
public class MyController {
@GetMapping("v1/remain")
public MyObject getRemain() {
// ...
}
}
如何在运行时通过Spring Boot动态启用或禁用此端点?另外,是否可以在不重新启动应用程序的情况下更改此设置?
答案 0 :(得分:2)
要在属性更改时动态重新加载Bean,可以使用Spring启动执行器+ Spring云,以便您可以访问/actuator/refresh
端点。
这可以通过添加以下依赖项来完成:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
后者确实要求您添加Spring云的BOM,即:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
现在,您可以通过设置以下属性来启用/actuator/refresh
端点:
management.endpoints.web.exposure.include=refresh
这将允许您向POST
发送/actuator/refresh
调用,这将返回所有已更改属性的数组。
通过使用/actuator/refresh
端点,它还允许您使用@RefreshScope
批注来重新创建bean。但是,有一些限制:
@RefreshScope
重新创建Bean,而不重新评估可能由于刷新而改变的条件。这意味着this solution与@RefreshScope
不兼容,如the comment section of this question所示。@RefreshScope
也不适合与过滤器配合使用,如this issue所示。这意味着您有两个选择:
将@RefreshScope
添加到控制器中,并自己执行条件逻辑,例如:
@RefreshScope
@RestController
@RequestMapping("/api/foo")
public class FooController {
@Value("${foo.controller.enabled}")
private boolean enabled;
@GetMapping
public ResponseEntity<String> getFoo() {
return enabled ? ResponseEntity.of("bar") : ResponseEntity.notFound().build();
}
}
这意味着您必须将此条件添加到控制器内的所有端点。我尚未验证您是否可以将其用于各个方面。
另一种解决方案是不使用@RefreshScope
,而是懒惰地获取要验证的属性。这使您可以将其与过滤器一起使用,例如:
public class FooFilter extends OncePerRequestFilter {
private Environment environment;
public FooFilter(Environment environment) {
this.environment = environment;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if ("true".equalsIgnoreCase(environment.getProperty("foo.controller.enabled"))) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.NOT_FOUND.value());
}
}
}
您还必须注册过滤器,例如使用:
@Bean
public FilterRegistrationBean<FooFilter> fooFilter(Environment environment) {
FilterRegistrationBean<FooFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new FooFilter(environment));
bean.addUrlPatterns("/api/foo");
return bean;
}
请注意,此方法仅从Environment
动态获取属性。刷新Environment
本身仍然需要您使用/actuator/refresh
端点。
答案 1 :(得分:0)
您可以使用@ConditionalOnExpression或@ConditionalOnProperty
@RestController
@ConditionalOnExpression("${my.property:false}")
@RequestMapping(value = "my-end-point", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController {
@RequestMapping(value = "endpoint1", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> endpoint1(
return new ResponseEntity<>("Hello world", HttpStatus.OK);
}
}
现在,如果要使以上控制器正常工作,则需要在application.properties文件中添加以下内容。
my.controller.enabled=true
没有上面的声明,它将表现为上面的控制器不存在。
类似地
@ConditionalOnProperty("my.property")
与上述行为完全相同;如果该属性存在且为“ true”,则该组件有效,否则无效。