Чтобы динамически перезагружать bean-компоненты при изменении свойства, вы можете использовать Spring boot привод + Spring Cloud, чтобы иметь доступ к конечной точке /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, а именно:
<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
воссоздает компонент без переоценки условий, которые могли измениться из-за обновления. Это означает, что это решение не работает с @RefreshScope
, как видно в разделе комментариев к этому вопросу .
@RefreshScope
тоже плохо работает с фильтрами, как видно из этой проблемы .
Это означает, что у вас есть два варианта:
Добавьте @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
.