目录
条件化创建bean的例子
使用
解析
-
条件化创建bean的例子
- 希望一个或多个bean只有在应用的类路径下包含特定的库时创建
- 希望某个bean只有当另外某个特定的bean也声明之后才会创建
- 要求某个特定的环境变量设置之后,才会创建某个bean
- 在Spring 4之前,很难实现这种级别的条件化配置
- 但是Spring 4引入了一个新的@Conditional 注解,它可以用到带有@Bean 注解的方法上
- 如果给定的条件计算结果为true,就会创建这个bean,否则的话,这个bean会被忽略
- 设想一种应用场景:应用部署在多台服务器上,而想应用的其中某个服务只运行在一台服务器上(比如定时任务或者kafka消息监听处理消息等服务),条件化的bean可以实现此类需求
- 比如应用上下文在创建某个bean之前,查询数据库,若数据库配置的此环境ip与当前虚机ip一致,则创建,否则则不创建
-
使用
- 自定义的条件类IpSameCondition需要实现Condition接口,重写matches()方法即可
- 可以看出来,这个接口实现起来很简单直接
- 只需提供matches()方法的实现即可
- 如果matches()方法返回true,那么就会创建带有@Conditional 注解的bean
- 如果matches()方法返回false,将不会创建这些bean
-
解析
- matches()方法很简单但功能强大
- 它能通过给定的ConditionContext对象进而得到Environment对象再进行检查
- 但Condition实现的考量因素可能会比这更多
- matches()方法会得到ConditionContext和AnnotatedTypeMetadata对象用来做出决策
- ConditionContext是一个接口,大致如下所示:
- 通过ConditionContext,我们可以做到如下几点:
- 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义
- 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性
- 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么
- 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源
- 借助getClassLoader()返回的ClassLoader加载并检查类是否存在
- AnnotatedTypeMetadata则能够让我们检查带有@Bean 注解的方法上还有什么其他的注解
- 像ConditionContext一样,AnnotatedTypeMetadata也是一个接口;它如下所示:
- 借助isAnnotated()方法,我们能够判断带有@Bean 注解的方法是不是还有其他特定的注解
- 借助其他的那些方法,我们能够检查@Bean 注解的方法上其他注解的属性
- 非常有意思的是,从Spring 4开始,@Profile 注解进行了重构,使其基于@Conditional 和Condition实现
- 作为如何使用@Conditional 和Condition的例子,我们来看一下在Spring 4中,@Profile 是如何实现的
- @Profile 本身也使用了@Conditional 注解,并且引用ProfileCondition作为Condition实现
- 如下所示,ProfileCondition实现了Condition接口,并且在做出决策的过程中,考虑到了ConditionContext和AnnotatedTypeMetadata中的多个因素
- ProfileCondition检查某个bean profile是否可用:
- 可以看到,ProfileCondition通过AnnotatedTypeMetadata得到了用于@Profile 注解的所有属性
- 借助该信息,它会明确地检查value属性,该属性包含了bean的profile名称
- 然后,它根据通过ConditionContext得到的Environment来检查[借助acceptsProfiles()方法]该profile是否处于激活状态