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