Spring 4 представляет новую функцию, называемую Conditional — аннотацию, нацеленную на компоненты Spring, которая генерирует bean-компоненты и запрещает генерацию этих bean-компонентов, по сути, она предоставляет способ условной генерации bean-компонентов.
Рассмотрим простой пример:
У меня есть служба под названием «CustomerService», с двумя реализациями этой службы, например, «CustomerService1» и «CustomerService2». Основываясь на наличии системного свойства, скажем «servicedefault», я хочу создать стандартную реализацию «CustomerService1», а если она отсутствует, я хочу создать экземпляр «CustomerService2».
Используя определение bean-компонента Spring на основе конфигурации, я бы сделал это следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
|
@Configurationpublic static class ContextConfig { @Bean public CustomerService customerService() { if (System.getProperty("servicedefault")!=null) { return new CustomerServiceImpl1(); } return new CustomerServiceImpl2(); }} |
Альтернативный подход заключается в использовании профилей Spring Bean, представленных в Spring 3.1:
|
01
02
03
04
05
06
07
08
09
10
11
|
@Bean@Profile("default")public CustomerService service1() { return new CustomerServiceImpl1();}@Bean@Profile("prod")public CustomerService service2() { return new CustomerServiceImpl2();} |
Тем не менее, профили в этом конкретном случае немного громоздки, так как будет сложно установить профиль для управления стратегией реализации одного компонента, он гораздо более уместен в тех случаях, когда необходимо контролировать поведение набора компонентов.
Spring 4 вводит условную аннотацию, где это поведение может быть достигнуто немного более многократно.
Условное зависит от набора классов Условий для указания предиката, следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
class HardCodedSystemPropertyPresentCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return (System.getProperty("servicedefault") != null); }}class HardCodedSystemPropertyAbsentCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return (System.getProperty("servicedefault") == null); }} |
Мне нужны два предиката, один для указания положительного условия и один для указания отрицательного условия, теперь они могут быть применены к определениям бина:
|
01
02
03
04
05
06
07
08
09
10
11
|
@Bean@Conditional(HardCodedSystemPropertyPresentCondition.class)public CustomerService service1() { return new CustomerServiceImpl1();}@Bean@Conditional(HardCodedSystemPropertyAbsentCondition.class)public CustomerService service2() { return new CustomerServiceImpl2();} |
Однако обратите внимание, что в коде Условия есть жестко запрограммированное имя системного свойства «servicedefault», которое можно устранить, используя метааннотации. Новая метааннотация может быть определена следующим образом:
|
1
2
3
4
5
6
7
|
@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Conditional(OnSystemPropertyCondition.class)public @interface ConditionalOnSystemProperty { public String value(); public boolean exists() default true;} |
Эта метааннотация ConditionalOnSystemProperty принимает два указанных пользователем атрибута — «значение» для имени системного свойства и «существует», чтобы проверить, существует ли свойство или проверить, что свойство не существует. Мета-аннотация помечена аннотацией @Conditional, которая указывает на класс Condition для запуска для bean-компонентов, аннотированных этой новой мета-аннотацией, класс Condition следующий:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class OnSystemPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName()); Boolean systemPropertyExistsCheck = (Boolean)attributes.get("exists"); String systemProperty = (String)attributes.get("value"); if ((systemPropertyExistsCheck && (System.getProperty(systemProperty) != null)) || (!systemPropertyExistsCheck && (System.getProperty(systemProperty) == null))) { return true; } return false; }} |
Логика здесь заключается в том, чтобы получить атрибуты, определенные в экземплярах @Bean, используя метааннотацию, и инициировать проверку на наличие или отсутствие системного свойства на основе дополнительного атрибута «exist». Эта повторно используемая мета-аннотация теперь может быть определена в экземплярах @Bean для условного создания bean-компонентов следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@Configurationpublic static class ContextConfig { @Bean @ConditionalOnSystemProperty("servicedefault") public CustomerService service1() { return new CustomerServiceImpl1(); } @Bean @ConditionalOnSystemProperty(value="servicedefault", exists=false) public CustomerService service2() { return new CustomerServiceImpl2(); }} |
Заворачивать
Пример здесь тривиален и, вероятно, не очень реалистичен и используется исключительно для демонстрации условной функции. Гораздо лучший пример в Spring 4 — это способ использования Conditional для изменения поведения самих профилей на основе Spring 3.1, о которых я упоминал ранее:
Профили теперь внутренне основаны на метааннотации. Условно:
|
1
2
3
4
|
@Conditional(ProfileCondition.class)public @interface Profile { String[] value();} |