Est-il possible de configurer un bean de telle manière qu'il ne soit pas utilisé par un groupe de profils? Actuellement, je peux le faire (je crois):
@Profile("!dev, !qa, !local")
Existe-t-il une notation plus nette pour y parvenir? Supposons que j'ai beaucoup de profils. De plus, si j'ai une implémentation simulée et concrète d'un service (ou autre), puis-je simplement annoter l'un d'entre eux et supposer que l'autre sera utilisé dans tous les autres cas? En d'autres termes, est-ce, par exemple, nécessaire:
@Profile("dev, prof1, prof2")
public class MockImp implements MyInterface {...}
@Profile("!dev, !prof1, !prof2") //assume for argument sake that there are many other profiles
public class RealImp implements MyInterface {...}
Puis-je simplement annoter l'un d'eux et coller un @Primary
annotation de l'autre à la place?
En substance, je veux ceci:
@Profile("!(dev, prof1, prof2)")
Merci d'avance!
La réponse courte est: vous ne pouvez pas.
Mais il existe une solution de contournement soignée qui existe grâce à la @Conditional
annotation.
public abstract class ProfileCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (matchProfiles(conditionContext.getEnvironment())) {
return ConditionOutcome.match("A local profile has been found.");
}
return ConditionOutcome.noMatch("No local profiles found.");
}
protected abstract boolean matchProfiles(final Environment environment);
}
public class DevProfileCondition extends ProfileCondition {
private boolean matchProfiles(final Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
return prof.equals("dev") || prof.equals("prof1")) || prof.equals("prof2"));
});
}
}
public class ProdProfileCondition extends ProfileCondition {
private boolean matchProfiles(final Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
return !prof.equals("dev") && !prof.equals("prof1")) && !prof.equals("prof2"));
});
}
}
@Conditional(value = {DevProfileCondition.class})
public class MockImpl implements MyInterface {...}
@Conditional(value = {ProdProfileCondition.class})
public class RealImp implements MyInterface {...}
Cependant, cette approche nécessite Springboot.
D'après ce que je comprends, ce que vous voulez faire est de pouvoir remplacer certains de vos beans par des beans stub/mock pour des profils spécifiques. Il existe 2 façons de résoudre ce problème:
La première option est faisable mais difficile. En effet, le comportement par défaut de Spring lors de la fourniture de plusieurs profils dans l'annotation @Profile
Est une condition OR
(pas une AND
comme vous en auriez besoin dans votre cas). Ce comportement de Spring est le plus intuitif, car idéalement chaque profil doit correspondre à chaque configuration de votre application (production, test unitaire, test d'intégration, etc.), donc un seul profil doit être actif à chaque fois. C'est la raison OR est plus logique que AND
entre les profils. Par conséquent, vous pouvez contourner cette limitation, probablement en imbriquant des profils, mais vous feriez votre configuration très complexe et moins maintenable.
Ainsi, je vous suggère de suivre la deuxième approche. Ayez un profil unique pour chaque configuration de votre application. Tous les beans qui sont identiques pour chaque configuration peuvent résider dans une classe qui n'aura pas de @Profile
Spécifié. En conséquence, ces beans seront instanciés par tous les profils. Pour les beans restants qui doivent être distincts pour chaque configuration différente, vous devez créer une classe @Configuration
Distincte (pour chaque profil Spring), en les ayant tous avec @Profile
Défini sur le profil correspondant. De cette façon, il sera très facile de traiter ce qui est injecté dans tous les cas.
Cela devrait être comme ci-dessous:
@Profile("dev")
public class MockImp implements MyInterface {...}
@Profile("prof1")
public class MockImp implements MyInterface {...}
@Profile("prof2")
public class MockImp implements MyInterface {...}
@Profile("the-last-profile") //you should define an additional profile, not rely on excluding as described before
public class RealImp implements MyInterface {...}
Enfin, l'annotation @Primary
Est utilisée pour remplacer un beans existant. Lorsqu'il y a 2 beans du même type, s'il n'y a pas d'annotation @Primary
, Vous obtiendrez une erreur d'instanciation de Spring. Si vous définissez une annotation @Primary
Pour l'un des beans, il n'y aura pas d'erreur et ce bean sera injecté partout où ce type est requis (l'autre sera ignoré). Comme vous le voyez, cela n'est utile que si vous avez un seul profil. Sinon, cela deviendra également compliqué comme premier choix.
TL; DR : Oui, vous le pouvez. Pour chaque type, définissez un bean pour chaque profil et ajoutez une annotation @Profile
Avec uniquement ce profil.