1. 程式人生 > >Spring高級裝配(二) 條件化的bean

Spring高級裝配(二) 條件化的bean

reg not att 忽略 () ext 什麽 lean run

如果你希望一個bean在特定的條件下才會出現:

  • 應用的類路徑下包含特定的庫時才創建
  • 只有當某個特定的bean也聲明之後才會創建
  • 某個特定的環境變量設定之後才創建某個bean

在Spring 4之前,很難實現這種級別的條件化配置,但是Spring4引入了一個新的@Conditional註解,它可以用到帶有@Bean註解的方法上。如果給定的條件計算結果為true,就會創建這個bean,否則的話,這個bean會被忽略。

示例:設置了magic環境屬性才去實例化MagicBean

1 @Bean
2 @Conditional(MagicExistsCondition.class
) // 條件化地創建bean 3 public MagicBean magicBean() { 4 return new MagicBean(); 5 }

這裏的@Conditional中給定了一個Class,它指明了條件,也就是MagicCondition。

Condition接口如下:

1 public interface Condition {
2     boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
3 }

設置給@Conditional的類可以使任意實現了Condition接口的類型。如此一來,只需要提供matches( )方法的實現即可。如果matches( )方法返回為true,那麽就會創建帶有@Conditional註解的bean。反之則不會創建這些bean。

MagicExistsCondition的實現:

1 public class MagicExistsCondition implements Condition {
2     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
3         Environment env = context.getEnvironment();
4         return env.containsProperty("magic");    // 檢查是否有magic屬性
5
} 6 }

上面的例子中只獲取了Environment,ConditionContext中的內容還挺豐富的,它是一個接口:

1 public interface ConditionContext {
2     BeanDefinitionRegistry getRegistry();
3     ConfigurableListableBeanFactory getBeanFactory();
4     Environment getEnvironment();
5     ResourceLoader getResourceLoader();
6     ClassLoader getClassLoader();
7 }

通過ConditionContext,可以拿到的資源有:

  • 借助getRegistry()返回的BeanDefinitionRegistry檢查bean的定義
  • 借助getBeanFactory()返回的ConfigurableListableBeanFactory檢查bean是否存在(這個屌),甚至探查bean的屬性
  • 借助getEnvironment()返回的Environment檢查環境變量是否存在已經它的值是什麽
  • 讀取並探查getResourceLoader()返回的ResourceLoader所加載的資源
  • 借助getClassLoader()返回的ClassLoader加載並檢查類是否存在

AnnotationedTypeMetadata則能夠讓我們檢查帶有@Bean註解的方法上還有什麽其他註解。

1 public interface AnnotatedTypeMetadata {
2     boolean isAnnotated(String var1);
3     Map<String, Object> getAnnotationAttributes(String var1);
4     Map<String, Object> getAnnotationAttributes(String var1, boolean var2);
5     MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);
6     MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
7 }

通過isAnnotated方法,我們能夠判斷帶有@Bean註解的方法是不是還有其他特定的註解。借助其他的方法,能夠檢查@Bean註解的方法上其他註解的屬性。

在Spring 4開始,@Profile註解進行了重構,使其基於@Conditional和Condition的實現。

在Spring 4中@Profile的實現如下:

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target({ElementType.TYPE, ElementType.METHOD})
3 @Documented
4 @Conditional({ProfileCondition.class})
5 public @interface Profile {
6     String[] value();
7 }

ProfileCondition:

 1 class ProfileCondition implements Condition {
 2     @Override
 3     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
 4         if (context.getEnvironment() != null) {
 5             MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
 6             if (attrs != null) {
 7                 for (Object value : attrs.get("value")) {
 8                     if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
 9                         return true;
10                     }
11                 }
12                 return false;
13             }
14         }
15         return true;
16     }
17 }

ProfileCondition通過AnnotatedTypeMetadata得到了用於@Profile註解的所有屬性。借助該信息,它會明確地檢查value屬性,該屬性包含了bean的profile名稱。然後根據ConditionContext得到的Environment來檢查(借助acceptsProfiles()方法)該profile是否處於激活狀態。

總結

  • @Conditional註解可以實現一個bean的條件化聲明
  • Profile在Spring 4中使用@Conditional進行了重構
  • 實現條件化配置中有兩個關鍵的接口:ConditionContext和AnnotatedTypeMetadata,在檢測條件的時候起了關鍵作用
  • 這裏沒有講在xml中如何實現條件化配置
  • 這裏發現用Java來進行config還是挺爽的

Spring高級裝配(二) 條件化的bean