Статьи

КАК: Зарегистрировать компоненты, используя @Conditional и Condition in Spring

Аннотация @Profile в Spring может использоваться для любых компонентов Spring (например, @Component , @Service , @Configuration и т. Д.), @Configuration являются кандидатами для автоматического обнаружения. Аннотация @Profile принимает один профиль или набор профилей, которые должны быть активными, чтобы аннотированный компонент имел право на автоопределение. Для данного @Profile({"p1", "!p2"}) регистрация произойдет, если профиль p1 активен или если профиль p2 не активен. Здесь или имеет решающее значение.

Но как этого добиться с помощью @Profile : мы хотим активировать данный компонент, если профиль p1 активен и профили p2 и p3 оба неактивны?

Давайте предположим следующую ситуацию: у нас есть интерфейс NotificationSender который реализуется с помощью:

  • SendGridNotificationSender — активен, только если sendgrid профиль sendgrid ,
  • EmailNotificationSender — активен, только если активен профиль email .
  • NoOpNotificationSender — активен, только если активен профиль development и ни один из sendgrid и email не активен

Кроме того: одновременно может быть зарегистрирован только один NotificationSender а профиль development может использоваться вместе с sendgrid и email .

В описанной выше ситуации использование аннотации @Profile кажется недостаточно. Может быть, я немного усложняю вещи, но на самом деле я действительно хотел достичь вышеизложенного, не вводя другой профиль. И как я это сделал?

Я использовал аннотацию Spring 4 @Conditional . @Conditional разрешить регистрацию компонента, когда все указанные Condition соответствуют:

1
2
3
4
5
@Component
@Conditional(value = NoOpNotificationSender.ProfilesCondition.class)
class NoOpNotificationSender extends NotificationSenderAdapter {
 
}

ProfilesCondition реализует интерфейс org.springframework.context.annotation.Condition :

1
2
3
4
5
6
public static class ProfilesCondition implements Condition {
    @Override
    public boolean matches(ConditionContext c, AnnotatedTypeMetadata m) {
 
    }
}

Целое решение проблемы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@Component
@Conditional(value = NoOpNotificationSender.ProfilesCondition.class)
class NoOpNotificationSender extends NotificationSenderAdapter {
 
    static class ProfilesCondition implements Condition {
        @Override
        public boolean matches(ConditionContext c, AnnotatedTypeMetadata m) {
            return accepts(c, Profiles.DEVELOPMENT)
                && !accepts(c, Profiles.MAIL)
                && !accepts(c, Profiles.SEND_GRID);
        }
 
        private boolean accepts(ConditionContext c, String profile) {
            return c.getEnvironment().acceptsProfiles(profile);
        }
    }
}

Другие компоненты будут активированы, когда соответствующий профиль активен:

01
02
03
04
05
06
07
08
09
10
11
@Component
@Profile(value = Profiles.SEND_GRID)
public class SendGridNotificationSender extends NotificationSenderAdapter {
 
}
 
@Component
@Profile(value = Profiles.MAIL)
class EmailNotificationSender extends NotificationSenderAdapter {
 
}

Примеры использования:

Активные профили боб
развитие NoOpNotificationSender
разработка, sendgrid SendGridNotificationSender
разработка, почта EmailNotificationSender
sendgrid SendGridNotificationSender
почта EmailNotificationSender

Что вы думаете? Как бы вы решили эту проблему?