Статьи

Весна 4 Условная

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

Ссылка: весна 4 Условно от нашего партнера JCG Биджу Кунджуммена в блоге all and sundry.