• Condition


    Profile是个好东西。通过Profile,我们可以非常方便地条件化Bean的创建,动态调整应用程序的功能。可是,Profile只能做些简单的条件化,对于复杂一点的条件化Profile是无法胜任的。比如现有这样的数据源创建需求:
    1.如果类路径存在DBCP的JAR包,则创建DBCP提供的BasicDataSource数据源。
    2.如果类路径没有DBCP的JAR包,则创建Spring提供的DriverManagerDataSource数据源。

    毫无疑问,这样的需求Profile是实现不了的。要想实现这样的需求,还得仰赖Spring提供的,专门用于Bean的条件化创建的,功能远比Profile强大的Condition。而这,需要我们做好两件事情:
    1.实现Condition接口,以描述Bean的创建条件。
    2.往配置方法添加@Conditional注解,告诉Spring容器创建Bean时以某个实现了Condition接口的类作为条件。

    Condition接口签名如下:

    1 @FunctionalInterface
    2 public interface Condition {
    3     boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    4 }

    其中定义的matches方法返回一个布尔类型的值:如果返回的是TRUE,说明条件成立,Spring容器将会创建相应的Bean;如果返回的是FALSE,说明条件失败,Spring容器不会创建相应的Bean

    另外,matches方法还接受两个参数:一个参数是ConditionContext类型的,能向我们提供一些诸如Bean的定义,环境变量之类的信息;一个参数是AnnotatedTypeMetadata类型的,能向我们提供一些诸如配置方法是否带有某种注解之类的信息。我们实现matches方法的时候,能用这些信息进行条件检查。

    因此,为了实现文章开头提到的需求,我们首先需要做的就是这样实现Condition接口:

     1 public class ConditionOnDriverManagerDataSource implements Condition {
     2     @Override
     3     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
     4         var dbcpFileName = "WEB-INF\\lib\\commons-dbcp2-2.8.0.jar";
     5         var resourceLoader = context.getResourceLoader();
     6         var resource = resourceLoader.getResource(dbcpFileName);
     7         return !resource.exists();
     8     }
     9 }
    10 
    11 public class ConditionOnBasicDataSource implements Condition {
    12     @Override
    13     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    14         var dbcpFileName = "WEB-INF\\lib\\commons-dbcp2-2.8.0.jar";
    15         var resourceLoader = context.getResourceLoader();
    16         var resource = resourceLoader.getResource(dbcpFileName);
    17         return resource.exists();
    18     }
    19 }

    这段代码定义的两个类都实现了Condition接口:

    1.ConditionOnDriverManagerDataSource能够检查类路径是否存在DBCP的JAR包:如果存在则返回FALSE;否则返回TRUE。这个条件能够告诉Spring容器只有类路径没有DBCP的JAR包时,才会创建相应的Bean
    2.ConditionOnBasicDataSource能够检查类路径是否存在DBCP的JAR包:如果存在则返回TRUE;否则返回FALSE。这个条件能够告诉Spring容器只有类路径存在DBCP的JAR包时,才会创建相应的Bean

    于是,我们完成了Condition接口的实现,该把它们交给@Conditional注解进行Bean的条件化配置了。@Conditional注解有个Class<T>类型的value属性,用于指定实现了Condition接口的类的Class对象,告诉Spring容器创建Bean时以哪个Condition作为条件。如下所示:

     1 @Configuration
     2 public class AppConfig {
     3     @Bean
     4     @Conditional(value = ConditionOnDriverManagerDataSource.class)
     5     public DataSource produceDataSource() {
     6         var dataSource = new DriverManagerDataSource();
     7         dataSource.setUsername("root");
     8         dataSource.setPassword("123456");
     9         dataSource.setUrl("jdbc:mysql://localhost:3306/sm_person_rest");
    10         dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    11         return dataSource;
    12     }
    13 
    14     @Bean
    15     @Conditional(value = ConditionOnBasicDataSource.class)
    16     public DataSource produceBasicDataSource() {
    17         var dataSource = new BasicDataSource();
    18         dataSource.setUsername("root");
    19         dataSource.setPassword("123456");
    20         dataSource.setUrl("jdbc:mysql://localhost:3306/sm_person_rest");
    21         dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    22         return dataSource;
    23     }
    24 }

    这段配置代码定义了两个方法:
    1.方法produceDataSource带有@Conditional(ConditionOnDriverManagerDataSource.class)注解。Spring容器瞧见这个注解之后,就会调用ConditionOnDriverManagerDataSource的matches方法进行条件判断:如果matches方法返回TRUE,则创建DriverManagerDataSource数据源;否则不创建。
    2.方法produceBasicDataSource带有@Conditional(ConditionOnBasicDataSource.class)注解。Spring容器瞧见这个注解之后,就会调用ConditionOnBasicDataSource的matches方法进行条件判断:如果matches方法返回TRUE,则创建BasicDataSource数据源;否则不创建。

    如此一来,Spring容器就能根据类路径是否存在DBCP的JAR包决定创建哪种数据源了。有趣的是,前文介绍的Profile其实也是Condition的一种实现。如下所示:

     1 @Target({ElementType.TYPE, ElementType.METHOD})
     2 @Retention(RetentionPolicy.RUNTIME)
     3 @Documented
     4 @Conditional({ProfileCondition.class})
     5 public @interface Profile {
     6     String[] value();
     7 }
     8 
     9 class ProfileCondition implements Condition {
    10     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    11         MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
    12         if (attrs != null) {
    13             Iterator var4 = ((List)attrs.get("value")).iterator();
    14 
    15             Object value;
    16             do {
    17                 if (!var4.hasNext()) {
    18                     return false;
    19                 }
    20 
    21                 value = var4.next();
    22             } while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
    23 
    24             return true;
    25         } else {
    26             return true;
    27         }
    28     }
    29 }

    至此,关于Condition的介绍也就告一段落了。下章,我们将会开始介绍混合配置。欢迎大家继续阅读,谢谢大家!

    返回目录    下载代码

  • 相关阅读:
    技术博客开博,谢谢大家
    数据库自动备份,crontab定时任务
    服务器端口对外开放(包括,mysql,django)
    记 第一次linux下简易部署 django uwsgi nginx
    Oracle Undo表空间使用情况分析
    Oracle自动启动脚本配置
    Oracle 12cR2 RAC节点查询GV$视图报错ORA-12805
    Oracle 12cR2 RAC集群安装指南
    基于Java SSM框架和layui构建的博客、论坛、新闻、文章随笔系统(包含前后台)
    SSM实现java开发电子手机商城在线商城系统源码 MySQL数据库
  • 原文地址:https://www.cnblogs.com/evanlin/p/16074463.html
Copyright © 2020-2023  润新知