2014-11-21 3 views
25

Весна Profile аннотация позволяет выбирать профили. Однако, если вы читаете документацию, это позволяет вам выбирать более одного профиля при операции ИЛИ. Если вы укажете @Profile («A», «B»), ваш компонент будет активен, если активен либо профиль A, либо профиль B.Весна: Как сделать AND в профилях?

Наш вариант использования отличается, мы хотим поддерживать версии TEST и PROD с несколькими конфигурациями. Поэтому иногда мы хотим использовать autowire bean-компонент только в том случае, если активны оба профиля TEST и CONFIG1.

Есть ли способ сделать это с помощью пружины? Каким будет самый простой способ?

+1

хорошо в документах, упомянутых как поведение 'и/или' для' @Profile ("a", "b") '. Разве это не то, что вы ищете? docs - 'Аналогично, если класс @Component или @Configuration помечен как @Profile ({" p1 "," p2 "}), этот класс не будет зарегистрирован/обработан, если только профили 'p1' и/или 'p2' не будут был активирован. –

+0

@JavaBond означает, что это оператор «ИЛИ», а не «И». Они просто хотели четко указать, что это не эксклюзивно или (xor) – Artem

+0

Я открыл билет для источника Spring для поддержки оператора «AND» для аннотации профиля: https://jira.spring.io/browse/SPR-12458 – Artem

ответ

19

Поскольку Spring не предоставляет функцию AND из коробки. Я бы посоветовал следующую стратегию:

В настоящее время @Profile аннотация содержит условную аннотацию @Conditional(ProfileCondition.class). В ProfileCondition.class он выполняет итерацию через профили и проверяет, активен ли профиль. Аналогичным образом вы можете создать свою собственную условную реализацию и ограничить регистрацию компонента. например

public class MyProfileCondition implements Condition { 

    @Override 
    public boolean matches(final ConditionContext context, 
      final AnnotatedTypeMetadata metadata) { 
     if (context.getEnvironment() != null) { 
      final MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); 
      if (attrs != null) { 
       for (final Object value : attrs.get("value")) { 
        final String activeProfiles = context.getEnvironment().getProperty("spring.profiles.active"); 

        for (final String profile : (String[]) value) { 
         if (!activeProfiles.contains(profile)) { 
          return false; 
         } 
        } 
       } 
       return true; 
      } 
     } 
     return true; 
    } 

} 

В своем классе:

@Component 
@Profile("dev") 
@Conditional(value = { MyProfileCondition.class }) 
public class DevDatasourceConfig 

Примечание: Я не проверил для всех случаев угловых (как нуль, длина проверки и т.д.). Но это может помочь.

+0

и как выполнить то же самое с конфигурацией xml? –

+1

Спасибо! В моем ответе немного улучшил свой код. –

7

Немного улучшенная версия @Mithun ответ:

public class AndProfilesCondition implements Condition { 

public static final String VALUE = "value"; 
public static final String DEFAULT_PROFILE = "default"; 

@Override 
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) { 
    if (context.getEnvironment() == null) { 
     return true; 
    } 
    MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); 
    if (attrs == null) { 
     return true; 
    } 
    String[] activeProfiles = context.getEnvironment().getActiveProfiles(); 
    String[] definedProfiles = (String[]) attrs.getFirst(VALUE); 
    Set<String> allowedProfiles = new HashSet<>(1); 
    Set<String> restrictedProfiles = new HashSet<>(1); 
    for (String nextDefinedProfile : definedProfiles) { 
     if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') { 
      restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length())); 
      continue; 
     } 
     allowedProfiles.add(nextDefinedProfile); 
    } 
    int activeAllowedCount = 0; 
    for (String nextActiveProfile : activeProfiles) { 
     // quick exit when default profile is active and allowed profiles is empty 
     if (DEFAULT_PROFILE.equals(nextActiveProfile) && allowedProfiles.isEmpty()) { 
      continue; 
     } 
     // quick exit when one of active profiles is restricted 
     if (restrictedProfiles.contains(nextActiveProfile)) { 
      return false; 
     } 
     // just go ahead when there is no allowed profiles (just need to check that there is no active restricted profiles) 
     if (allowedProfiles.isEmpty()) { 
      continue; 
     } 
     if (allowedProfiles.contains(nextActiveProfile)) { 
      activeAllowedCount++; 
     } 
    } 
    return activeAllowedCount == allowedProfiles.size(); 
} 

} 

Не смог отправить его в комментариях.

5

Если вы уже отмечали класс конфигурации или метод компонента с @Profile аннотацию, просто чтобы проверить наличие дополнительных профилей (например, для и условий) с Environment.acceptsProfiles()

@Autowired Environment env; 

@Profile("profile1") 
@Bean 
public MyBean myBean() { 
    if(env.acceptsProfiles("profile2")) { 
     return new MyBean(); 
    } 
    else { 
     return null; 
    } 
} 
6

Еще один вариант, чтобы играть на уровень класса/метода, разрешенный аннотацией @Profile. Не так гибко, как реализация MyProfileCondition, но быстрая и чистая, если это подходит вашему делу.

например. это не будет запускаться, когда БЫСТРО & DEV оба активны, но если только DEV является:

@Configuration 
@Profile("!" + SPRING_PROFILE_FAST) 
public class TomcatLogbackAccessConfiguration { 

    @Bean 
    @Profile({SPRING_PROFILE_DEVELOPMENT, SPRING_PROFILE_STAGING}) 
    public EmbeddedServletContainerCustomizer containerCustomizer() { 
5

я улучшил ответ @ rozhoc в так что ответ не учитывает тот факт, что ни один профиль не эквивалентно «по умолчанию» когда дело доходит до использования @Profile. Кроме того, условия, которые я хотел, были !default && !a, которые не соответствовали правилу rozhoc. Наконец, я использовал некоторые Java8 и для краткости покажу только метод matches.

@Override 
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) { 
    if (context.getEnvironment() == null) { 
     return true; 
    } 
    MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); 
    if (attrs == null) { 
     return true; 
    } 

    Set<String> activeProfilesSet = Arrays.stream(context.getEnvironment().getActiveProfiles()).collect(Collectors.toSet()); 
    String[] definedProfiles = (String[]) attrs.getFirst(VALUE); 
    Set<String> allowedProfiles = new HashSet<>(1); 
    Set<String> restrictedProfiles = new HashSet<>(1); 
    if (activeProfilesSet.size() == 0) { 
     activeProfilesSet.add(DEFAULT_PROFILE); // no profile is equivalent in @Profile terms to "default" 
    } 
    for (String nextDefinedProfile : definedProfiles) { 
     if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') { 
      restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length())); 
      continue; 
     } 
     allowedProfiles.add(nextDefinedProfile); 
    } 
    boolean allowed = true; 
    for (String allowedProfile : allowedProfiles) { 
     allowed = allowed && activeProfilesSet.contains(allowedProfile); 
    } 
    boolean restricted = true; 
    for (String restrictedProfile : restrictedProfiles) { 
     restricted = restricted && !activeProfilesSet.contains(restrictedProfile); 
    } 
    return allowed && restricted; 
} 

Вот как вы на самом деле использовать его в том случае, сбивает с толку, а также:

@Profile({"!default", "!a"}) 
@Conditional(value={AndProfilesCondition.class}) 
+0

rozhok, not rozhoc pls. –

+0

Эй, это сработало. Победа! – sbzoom

2

Другой вид трюк, но может работать во многих сценариях ставится @Profile аннотацию на @Configuration и другой @Profile на @Bean - создает логическое И между двумя профилями в java-основе Spring config.

@Configuration 
@Profile("Profile1") 
public class TomcatLogbackAccessConfiguration { 

    @Bean 
    @Profile("Profile2") 
    public EmbeddedServletContainerCustomizer containerCustomizer() {