Включение и отключение конечных точек во время выполнения при загрузке Spring - PullRequest
1 голос
/ 02 июня 2019

Допустим, у меня есть следующий контроллер:

@RestController
public class MyController {
    @GetMapping("v1/remain")
    public MyObject getRemain() {
        // ...
    }
}

Как я могу включить или отключить эту конечную точку во время выполнения динамически с загрузкой Spring?Кроме того, возможно ли изменить это без перезапуска приложения?

Ответы [ 2 ]

2 голосов
/ 03 июня 2019

Чтобы динамически перезагружать 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-компонентов. Однако есть несколько ограничений:

  1. @RefreshScope воссоздает компонент без переоценки условий, которые могли измениться из-за обновления. Это означает, что это решение не работает с @RefreshScope, как видно в разделе комментариев к этому вопросу .
  2. @RefreshScope тоже плохо работает с фильтрами, как видно из этой проблемы .

Это означает, что у вас есть два варианта:

  1. Добавьте @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();
        }
    }
    

    Это означает, что вам придется добавить это условие ко всем конечным точкам вашего контроллера. Я не проверял, можно ли использовать это с аспектами.

  2. Другое решение - не использовать @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.

0 голосов
/ 02 июня 2019

Вы можете использовать @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», компонент работает, в противном случае это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...