Исключить @Configuration из приложения Spring Boot

#java #spring #spring-boot

#java #spring #spring-загрузка

Вопрос:

У меня есть куча модулей (скажем, 3). Два модуля основаны на Spring boot, а другой — на Spring. Скажем, модуль 1 — Модуль SpringBoot 2 — Модуль Spring Boot 3 — Общий модуль, основанный только на Spring

Определен модуль 3 @Файл конфигурации, который должен выбираться только модулем 2, а не 1.

Я перепробовал кучу вещей, чтобы исключить файл конфигурации. Например:-

 @SpringBootApplication
@ComponentScan(basePackages = {"com.adobe"}
, excludeFilters = {
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {WorkerConfig.class, WorkerExecutors.class, Worker.class})})
public class Application {

  private static final Logger logger = LoggerFactory.getLogger(Application.class);

  public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
  }

}
  

Но все же класс @Configiration не исключается, и Spring пытается загрузить его в контексте приложения, чего я не хочу. Мой класс конфигурации

 @Configuration
public class WorkerConfig {

  @Bean
  public WorkerExecutors workerExec() {
    WorkerExecutors executors = new WorkerExecutors();
    return executors;
  }

}
  

Также я читаю в аннотации @ComponentScan, что

  * <p>Note that the {@code <context:component-scan>} element has an
 * {@code annotation-config} attribute; however, this annotation does not. This is because
 * in almost all cases when using {@code @ComponentScan}, default annotation config
 * processing (e.g. processing {@code @Autowired} and friends) is assumed. Furthermore,
 * when using {@link AnnotationConfigApplicationContext}, annotation config processors are
 * always registered, meaning that any attempt to disable them at the
 * {@code @ComponentScan} level would be ignored.
  

Похоже, исключение при проверке компонентов не сработает. Помимо описанного выше, я также пытался исключить использование

 @SpringBootApplication(exclude= {WorkerExecutors.class, Worker.class,WorkerConfig.class})
public class Application {
  

Но spring boot выдает

 java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
    - com.adobe.repository.worker.lib.config.WorkerConfig
    - com.adobe.acp.repository.worker.lib.core.WorkerExecutors
    - com.adobe.acp.repository.worker.lib.core.Worker
  

Любая идея, как я могу отключить компонентное сканирование модуля загрузки, отличного от spring, в модуле загрузки spring, кроме установки в другой пакет. Я не хочу помещать в другой пакет.

Любая помощь приветствуется!!

Комментарии:

1. Несколько хакерским способом сделать это было бы снабдить Worker конфигурации @ConditionalOnProperty аннотацией, а затем установить свойство только для модуля 2.

2. Да, это работает.. Я использовал conditional, а не condition by property, поскольку это приводит к зависимости spring boot, которой я хотел избежать .. но вы говорите, что это халтурно?

3. Это своего рода хакерство, потому что, как упоминалось в некоторых других комментариях, в идеале вы бы разделили свои модули так, чтобы только код, который должен находиться в classpath, фактически находился в classpath (т. Е. модуль 1 даже не должен иметь доступа к нежелательным конфигурационным классам модуля 3). Используя «поэтапное» сканирование компонентов, подобное этому, вы можете столкнуться с ошибками / проблемами в будущем, когда что-то от Worker действительно должно присутствовать в модуле 1, в конце концов, и это заставит вас переработать этот подход. Но если у вас это работает и вы можете справиться с этими проблемами, это тоже правильный путь.

4. Соглашайтесь с этим. Но на данный момент это одноразовые классы, поэтому нет особого смысла перемещать их из classpath. Но да, когда подобные вещи увеличиваются, определенно, код может давать ошибки и его трудно поддерживать.

Ответ №1:

Вы можете попробовать это, у меня это работает:

 @SpringBootApplication
@ComponentScan(excludeFilters  = {@ComponentScan.Filter(
              type = FilterType.ASSIGNABLE_TYPE, classes = {WorkerConfig.class, WorkerExecutors.class, Worker.class})})
  

Комментарии:

1. На самом деле этого не будет.

2. Я обнаружил, что это сработало, только если я указал basePackages свойство в @ComponentScan аннотации.

Ответ №2:

Пакеты автоматической настройки находятся в org.springframework.boot.autoconfigure. Вот причина, по которой вы не можете сделать:

 @SpringBootApplication(exclude= {WorkerExecutors.class, Worker.class,WorkerConfig.class})
  

Spring делает то, что вы говорите делать. Вы вызываете:

 @ComponentScan(basePackages = {"com.adobe"}
, excludeFilters = {
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {WorkerConfig.class, WorkerExecutors.class, Worker.class})})
  

Поэтому Spring не будет загружать ни один из этих рабочих классов. Вот почему Spring не «выполняет» ваши классы, помеченные @Configuration.

Тем не менее, то, что вы пытаетесь сделать, для меня не имеет смысла. Похоже, у вас есть «модули» (классы Java), но все они являются частью одного и того же контекста spring. Если у вас есть единственный контекст Spring, то вы можете указать Spring загружать некоторые классы @Configuration, а не некоторые другие. Затем из ваших «модулей» вы можете вводить все, что вам нужно. Модуль 1 будет вводить компоненты из модуля 3, а модуль 2 — нет. Вот так просто.

Если по какой-то причине вам действительно нужно запретить модулю 2 доступ к компонентам из модуля 3, но при этом сохранить модуль 3 видимым из модуля 1, то я бы разделил модуль 1 и модуль 2 в двух разных приложениях Spring Boot, а модуль 3 стал общим кодом. Но этот подход может нарушить вашу текущую архитектуру.

ОБНОВЛЕНИЕ От 29 марта 2019 г.

Попробуйте это:

 @SpringBootApplication
@ComponentScan(basePackages = { "com.myapp" }, excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = { MyClass2.class }) })
  

У меня это сработало. У меня есть MyClass и MyClass2, и MyClass загружен, а MyClass2 — нет. Я попробовал это с Spring Boot 1.5.9.RELEASE и Spring Bom для всех зависимостей.

Комментарии:

1. Spring загружает рабочие классы, вот в чем проблема. Я хочу, чтобы он не загружал их, но текущий код не работает. Кроме того, думаю, я не был подробен в своем вопросе. Модуль 1 и модуль 2 являются отдельными приложениями Spring boot, а модуль 3 представляет собой общий код. Здесь находятся мои рабочие классы (включая config). Они хотят загружаться модулем 2 и другими модулями, но не модулем 1, но некоторые другие классы конфигурации, присутствующие в модуле 3, требуются модулю 1. Конечно, я также могу разделить модуль 3 на модуль 4, где модуль 1 никогда не сканирует 4, но я смотрю, как это сделать в текущей структуре кода, о которой я упоминаю

2. Что ж, если модуль 3 содержит код, который необходим в модуле 2, но не в модуле 1, то модуль 3 действительно является обычным кодом. Я знаю, что вы это знаете, просто указывая на проблему. Теперь, по моему опыту, использование @Configuration в обычном коде не так уж хорошо, потому что оно будет сканироваться всеми подпроектами (или модулями, или как вы хотите это назвать). Что мне очень помогло, так это поместить код в общее место, но не аннотации Spring, а просто код. Допустим, у вас есть модуль 3 с кучей кода Java, но без аннотаций Spring. Продолжайте читать следующий комментарий ->

3. Затем, если модулю 2 требуется код из модуля 3, то вам нужно создать класс конфигурации в модуле 2, который создает все @Beans, используя типы / код из модуля 3. Имеет смысл? Это правильный путь.

4. Звучит справедливо. Но единственное, что модуль 3 будет вызываться не только модулем 2, но и другими модулями (4,5), так что on..so в конечном итоге мы будем дублировать код во всех модулях.. это то, чего я хочу избежать..

5. Ну, на самом деле это не дублирующий код. Краткий пример: Раньше я работал в проекте, в котором было около 10 приложений springboot. Все они были настроены на использование компонента фильтра ключей API. Итак, логика фильтрации ключей API была помещена в класс, называемый APIKeyFilter, внутри общего модуля Java. Затем каждое приложение Spring Boot создало бы компонент, подобный этому: @Bean public APIKeyFilter apikeyfilter(){}