2014-11-21 5 views
25

Spring Profile Anmerkung ermöglicht die Auswahl von Profilen. Wenn Sie jedoch Dokumentation lesen, können Sie nur mehr als ein Profil mit OR-Operation auswählen. Wenn Sie @Profile ("A", "B") angeben, ist Ihre Bean aktiv, wenn entweder Profil A oder Profil B aktiv ist.Frühling: Wie man UND in Profilen macht?

Unser Anwendungsfall ist anders, wir möchten TEST- und PROD-Versionen mehrerer Konfigurationen unterstützen. Daher möchten wir die Bean manchmal nur dann automatisch ansteuern, wenn beide Profile TEST und CONFIG1 aktiv sind.

Gibt es eine Möglichkeit, es mit Feder zu tun? Was wäre der einfachste Weg?

+1

gut in docs genannt als 'und/oder' Verhalten für '@Profile (" a "," b ")'. Ist das nicht wonach Sie suchen? docs - 'Wenn eine @Component- oder @Configuration-Klasse mit @Profile ({" p1 "," p2 "}) markiert ist, wird diese Klasse ebenso nicht registriert/verarbeitet, es sei denn, die Profile 'p1' und/oder 'p2' haben aktiviert worden. " –

+0

@ JavaBond was bedeutet, dass es" ODER "Operator und nicht" UND "ist. Sie wollten nur explizit darauf hinweisen, dass es nicht exklusiv ist oder (xor) – Artem

+0

Ich öffnete ein Ticket für Spring Source, um "AND" -Operator für Profil Annotation zu unterstützen: https://jira.spring.io/browse/SPR-12458 – Artem

Antwort

19

Seit Frühling bietet die AND-Funktion nicht aus der Box. Ich würde vorschlagen, die folgende Strategie:

Derzeit @Profile Annotation hat eine bedingte Annotation @Conditional(ProfileCondition.class). In ProfileCondition.class iteriert es durch die Profile und prüft, ob das Profil aktiv ist. Ähnlich könnten Sie Ihre eigene bedingte Implementierung erstellen und die Registrierung der Bean einschränken. z.B.

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; 
    } 

} 

In Ihrer Klasse:

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

ANMERKUNG: Ich habe nicht für alle Sonderfälle (wie null, Länge Schecks etc.) geprüft. Aber diese Richtung könnte helfen.

+0

und wie erreiche ich das gleiche mit XML-Konfiguration? –

+1

Danke! Mein Code in meiner Antwort wurde ein wenig verbessert. –

7

Ein wenig verbesserte Version von @Mithun Antwort:

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(); 
} 

} 

konnte es in den Kommentaren posten.

5

Wenn Sie bereits eine Konfigurationsklasse oder Bean-Methode mit @Profile Annotation markiert, ist es einfach für zusätzliche Profile (zB für UND-Bedingung) zu prüfen, mit Environment.acceptsProfiles()

@Autowired Environment env; 

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

Noch eine andere Option zu spielen ist die Klassen-/Methodenebene, die von der @Profile Annotation zulässig ist. Nicht so flexibel wie die Implementierung MyProfileCondition, aber schnell und sauber, wenn es zu Ihrem Fall passt.

z.B. dies wird nicht gestartet, wenn FAST & DEV beide aktiv sind, wird aber, wenn nur DEV ist:

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

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

I @ rozhoc Antwort verbessert, da diese Antwort nicht die Tatsache berücksichtigen haben, dass kein Profil auf ‚default‘ entspricht wenn es um @Profile geht. Auch Bedingungen, die ich wollte, waren !default && !a, die @ rozhocs Code nicht richtig behandelt. Schließlich habe ich einige Java8 verwendet und zeige nur die matches Methode der Kürze halber.

@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; 
} 

Hier ist, wie Sie es eigentlich für den Fall verwenden, als auch verwirrend war:

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

rozhok, nicht rozhoc pls. –

+0

Hey, das hat funktioniert. Gewinnen! – sbzoom

2

Eine andere Art von Trick könnte aber in vielen Szenarien arbeiten @Profile Anmerkung auf @Configuration gesetzt wird und die andere @Profile auf @Bean - das schafft logische AND zwischen 2 Profilen in java-basierter Spring-Konfiguration.

Verwandte Themen