Spring高级装配(二) 条件化的bean
Spring高级装配(二) 条件化的bean
如果你希望一个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还是挺爽的
</div>
<div class="postDesc">posted @ <span id="post-date">2018-07-03 15:44</span> <a href="https://www.cnblogs.com/tuhooo/">tuhooo</a> 阅读(<span id="post_view_count">40</span>) 评论(<span id="post_comment_count">0</span>) <a href="https://i.cnblogs.com/EditPosts.aspx?postid=9258962" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(9258962);return false;">收藏</a></div>
</div>
<script type="text/javascript">var allowComments=true,cb_blogId=243049,cb_entryId=9258962,cb_blogApp=currentBlogApp,cb_blogUserGuid='d37e2fdf-0851-e511-b908-9dcfd8948a71',cb_entryCreatedDate='2018/7/3 15:44:00';loadViewCount(cb_entryId);var cb_postType=1;</script>