@Conditional

转载自:https://blog.youkuaiyun.com/doctor_who2004/article/details/79184230

@Conditional注解可以根据任何环境条件来注册bean,是否注册bean实例的条件有如下几个:

  • 在classpath路径中是否存在某个特定的类
  • 在ApplicationContext中是否还没注册过一个特定类型的bean
  • 是否在某路径下存在某文件
  • 是否在配置文件中配置了某特定的属性
  • 是否存在某特定的系统环境变量

下面举些例子:
1、根据是否存在某特定的系统环境变量来注册bean
  假如应用程序既可以使用mysql数据库,也可以使用Mongo数据库,需要根据系统环境变量dbType来使用不同的数据库。
  为了利用spring boot的@Conditional注解决定实例化哪个数据源api,要实现判断条件:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class MysqlDbTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        final String dbType = System.getProperty("dbType");
        return "mysql".equalsIgnoreCase(dbType);
    }
}
public class MongoDbTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        final String dbType = System.getProperty("dbType");
        return "mongo".equalsIgnoreCase(dbType);
    }
}

定义了两个Condition后,需要在对应的dao层注解:

public interface UserDao {
    List<String> getAllUserNames();
}
@Repository
@Conditional(MongoDbTypeCondition.class)
public class MongoUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList("Mongo db", "test");
    }
}
@Repository
@Conditional(MysqlDbTypeCondition.class)
public class MysqlUserDaoImpl implements UserDao {
    @Override
    public List<String> getAllUserNames() {
        return Arrays.asList("mysql", "test");
    }
}

之后,只需要在程序启动时,加上参数 -DdbType=xxx 来指定即可。

2、根据在classpath路径中是否存在某个特定的类来注册bean
  还可以根据在classpath路径中是否存在某个特定的类来注册bean,还是由以上的代码来改动。假如,classpath路径下出现mysql驱动类com.mysql.jdbc.Driver就实例化MysqlUserDaoImpl,否则就默认实例化MongoUserDaoImpl,现在只需要修改判断条件即可:

public class MysqlDbTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
public class MongoDbTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            return false;
        } catch (ClassNotFoundException e) {
            return true;
        }
    }
}

3、其它条件类推
  想要实现其它的条件的推断,只需要按照需求实现Condition接口即可。

Spring boot提供的@Conditional注解有很多,例如**@ConditionalOnClass**、@ConditionalOnBean,按需取用。例如直接使用@ConditionalOnProperty(name = "dbType", havingValue = "mongo") 同样可以达到示例一实现的功能。


条件注解 @ConditionalOnBean 的正确使用姿势

在使用 @ConditionalOnBean 注解时会遇到不生效的情况,依赖的 bean 明明已经配置了,但就是不生效。是不是@ConditionalOnBean和 Bean加载的顺序有没有关系呢?问题演示:

@Configuration
public class Configuration1 {

    @Bean
    @ConditionalOnBean(Bean2.class)
    public Bean1 bean1() {
        return new Bean1();
    }
}

@Configuration
public class Configuration2 {

    @Bean
    public Bean2 bean2(){
        return new Bean2();
    }
}

运行结果:@ConditionalOnBean(Bean2.class)返回false。明明定义的有bean2,bean1却未加载。

原因在于spring ioc的过程中,优先解析@Component,@Service,@Controller注解的类。其次解析配置类,也就是@Configuration标注的类。最后开始解析配置类中定义的bean。
示例代码中bean1是定义在配置类中的,当执行到配置类解析的时候,@Component,@Service,@Controller ,@Configuration标注的类已经全部扫描,所以这些BeanDifinition已经被同步。 但是bean1的条件注解依赖的是bean2,bean2是被定义的配置类中的,所以此时配置类的解析无法保证先后顺序,就会出现不生效的情况。

解决:项目中条件注解依赖的类,大多会交给spring容器管理,所以如果要在配置中Bean通过@ConditionalOnBean依赖配置中的Bean时,完全可以用@ConditionalOnClass(Bean2.class)来代替。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值