文章目录

  • 前言
    • 简介
    • 创建 springboot-condition 模块
    • 开始操作
      • 获取容器中的 Bean
      • @Conditional() 注解
        • matches() 条件判断
          • 导入 Jedis 坐标后
          • 导入通过注解属性值 value 指定坐标
        • 思考
        • spring-boot-autoconfigure
          • condition 包
            • ConditionalOnClass
            • ConditionalOnBean
            • ConditionalOnProperty
            • ConditionalOnMissingBean
          • data 包
        • 演示
  • 总结
    • 自定义条件
    • SpringBoot 提供的常用条件注解

前言

SpringBoot 的自动配置分为 Condition、切换内置web服务器、Enable 注解、Import 注解、EnableAutoConfiguration 注解。
本章分享的是 Condition 的内容。

简介

Condition 是在 Spring 4.0 增加的条件判断功能,通过这个功能可以实现选择性的创建 Bean 操作。

创建 springboot-condition 模块

使用 IDEA 快速创建一个 SpringBoot 模块,不引入任何依赖。

开始操作

进入 SpringBoot 启动类,点击进入 run() 可以看到这个方法是有返回值的,返回值为 ConfigurableApplicationContext,这个返回值就是 IOC 容器。


获取 run() 的返回值。

// 启动SpringBoot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);

获取容器中的 Bean

在 pom.xml 中导入 Redis 的起步依赖。

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>

在启动类中编写获取 redisTemplate 代码。

// 获取Bean,redisTemplate
Object redisTemplate = context.getBean("redisTemplate");
System.out.println(redisTemplate);

打印输出获取到的 redisTemplate。

接下来创建 domain -> User 类

package com.xh.springbootcondition.domain;public class User {}

创建config -> UserConfig 类,类中添加返回值为 User 的方法,方法顶部添加 @Bean。

package com.xh.springbootcondition.config;import com.xh.springbootcondition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfig {@Beanpublic User user(){return new User();}
}

在启动类中获取容器中的 user 对象并打印输出。

// 获取Bean,user
Object user = context.getBean("user");
System.out.println(user);

@Conditional() 注解

Spring 在创建 Bean 时,我们可以在 UserConfig 中的 user 方法上添加条件注解 @Conditional()。

@Conditional() 注解点进去可以看到这个注解需要一个 Class(因为是一个数组,所以可以导很多 Class ),这些 Class 必须都是 Condition 或者 Condition 的子类。

而 Condition 就是核心的条件接口,点击进入 Condition 可以看到接口中只有一个 matches() 方法,返回值为 boolean 类型。

所以我们要使用 @Conditional() 注解就需要在注解中传入一个 Condition 条件接口的实现类,并且实现类要复写matches() ,返回 true 或者 false。
如果返回的是 true,那么 User 对应的 Bean 将会被 Spring 容器创建,如果返回的是 false,那么容器则不会创建 User 对应的 Bean 。
创建 condition -> ClassCondition 类,实现 Condition 接口,复写 matches() 。

package com.xh.springbootcondition.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class ClassCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return false;}
}

在 user 方法顶部的 @Conditional() 注解中放入创建好的 ClassCondition.class

@Conditional(ClassCondition.class)

由于我们的 ClassCondition 实现类中复写的 matches() 返回的是 false,那么 Spring 容器就不会创建 User 所对应的 Bean,测试运行输出会提示找不到 user。

如果把返回条件改为 true,那么 User 对应的 Bean 就会被创建。

matches() 条件判断

我们可以在 复写的 matches() 中加入条件判断:

导入 Jedis 坐标后

在 pom.xml 中导入 Jedis 坐标

        <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>

判断是否导入 Jedis 坐标(仅用于测试用例,不推荐用来实际操作)

        // 1.需求:导入 Jedis 坐标后,创建 Bean// 思路:判断 redis.clients.jedis.Jedis.class 是否存在boolean flag = true;try {Class<?> cls = Class.forName("redis.clients.jedis.Jedis");} catch (ClassNotFoundException e) {flag = false;e.printStackTrace();}return flag;

运行启动类,成功输出 user。

如果把 pom.xml 文件中的 Jedis 坐标注释掉,那么Spring 容器则不会创建 User 对应的 Bean,运行启动类就获取不到 user 对象。

导入通过注解属性值 value 指定坐标

上一种方法操作空间有限,接下来我们去自定义一个注解来实现动态导入指定值判断是否创建Bean。
创建 condition -> ConditionOnClass 定义一个注解,添加一些相对应的注解并定义 value 属性。

package com.xh.springbootcondition.condition;import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;/*** @Target 表示当前注解可以加到哪个范围上* ElementType.TYPE 类上* ElementType.METHOD 方法上* --------------------------------------* @Retention 表示注解生效的时机,为 RUNRIME* --------------------------------------* @Documented 表示生成 javadoc 的文档*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {String[] value();
}

修改 UserConfig 类中 user(),使用自定义的注解。

package com.xh.springbootcondition.config;import com.xh.springbootcondition.condition.ConditionOnClass;
import com.xh.springbootcondition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfig {@Bean
//    @Conditional(ClassCondition.class)@ConditionOnClass("redis.clients.jedis.Jedis")public User user(){return new User();}
}

修改 ClassCondition 中 matches() 实现动态获取注解属性值。
修改前我们需要了解 matches() 中的两个参数:
ConditionContext context 和 AnnotatedTypeMetadata metadata。

package com.xh.springbootcondition.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;public class ClassCondition implements Condition {/*** 参数说明* @param context 上下文对象。用于获取环境,IOC 容器,ClassLoader 对象* @param metadata 注解元对象。可以获取注解定义的属性值。* @return*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {/*// 1.需求:导入 Jedis 坐标后,创建 Bean// 思路:判断 redis.clients.jedis.Jedis.class 是否存在boolean flag = true;try {Class<?> cls = Class.forName("redis.clients.jedis.Jedis");} catch (ClassNotFoundException e) {flag = false;e.printStackTrace();}return flag;*/// 2.需求:导入通过注解属性值 value 指定坐标后创建 Bean// 获取注解属性值 value[]Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());// System.out.println(map);String[] value = (String[]) map.get("value");boolean flag = true;try {for (String className : value) {Class<?> cls = Class.forName(className);}} catch (ClassNotFoundException e) {flag = false;e.printStackTrace();}return flag;}
}

运行启动类,输入获取的 user。

可以测试一个其他的坐标看看能不能创建出 User。
在 pom.xml 中添加 fastjson 坐标

        <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.4</version></dependency>

修改 userConfig,将之前编写的 user 方法注释掉,重新编写一个方法,@ConditionOnClass 的 value 值放入 com.alibaba.fastjson.JSON 路径。

@Configuration
public class UserConfig {//    @Bean
    @Conditional(ClassCondition.class)
//    @ConditionOnClass("redis.clients.jedis.Jedis")
//    public User user(){//        return new User();
//    }@Bean@ConditionOnClass("com.alibaba.fastjson.JSON")public User user(){return new User();}
}

运行启动类,正确输出:

思考

读到这里的朋友可以回过头思考一下:根据我们之前在启动类中编写的获取 redisTemplate 的代码:

// 启动SpringBoot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
// 获取Bean,redisTemplate
Object redisTemplate = context.getBean("redisTemplate");

SpringBoot 是如何知道要创建 RedisTemplate 的?
其实 SpringBoot 就是使用 Condition 去判断我们当前环境中有没有导入 Redis 的 start,如果导入了,那么就会创建 RedisTemplate。

spring-boot-autoconfigure

在 SpringBoot 中提供了非常多的 ConditionOn* 的类,我们可以在 External Libraries 中找到 spring-boot-autoconfigure 这个 SpringBoot 的自动配置 jar 包,其中有一个 condition 包,可以看到这个包里有很多 ConditionOn*。

condition 包
ConditionalOnClass

进入 ConditionalOnClass 中可以看到,他里面定义了一个注解,注解上面有一个 @Conditional,value 值为 OnClassCondition.class,这个操作就是在判断是否有对应的字节码文件。

我们可以再看一看其他的,比如:

ConditionalOnBean

这个注解代表了:当你的环境中有 OnBeanCondition 这个 Bean 的时候,才会去创建 ConditionalOnBean 这个注解修饰的 Bean。

ConditionalOnProperty

当你的配置文件中配置了指定的属性,才会创建某一个 Bean。

ConditionalOnMissingBean

当你的 IOC 容器中没有指定 Bean 的时候,才会去初始化指定的 Bean。

data 包

data 包中有一个 redis 的包,redis 有一个配置类为 RedisAutoConfiguration,如图可以看到这个配置类在初始化 RedisTemplate,并且有一些初始化的条件:

@ConditionalOnClass(RedisOperations.class)
在当前环境中需要有 RedisOperations 字节码存在,才能去创建下面方法的 Bean。(因为这个注解是加在类上的,所以下面所有的方法都要满足这个条件才可以创建 Bean)
@ConditionalOnMissingBean(name = “redisTemplate”)
可以看到在 redisTemplate 方法上添加了一个这样的注解,他代表如果当前环境中不存在 redisTemplate,才会创建 RedisTemplate,如果存在,那就证明我们自己定义了 RedisTemplate,则不会创建。

演示

修改 UserConfig,添加一个 user2 方法,并且在方法上添加 @Bean 和 @ConditionalOnProperty(name = “csdn”, havingValue = “xinghe”) 注解。
上面讲到 @ConditionalOnProperty() 是当配置文件中某一个属性和对应的值存在,才会去加载 Bean,这个注解表示:当我们的配置文件中有一个键为 ”csdn“,值为 ”xinghe“ 的时候,才会去加载对应的 Bean。

@Configuration
public class UserConfig {//    @Bean
    @Conditional(ClassCondition.class)
//    @ConditionOnClass("redis.clients.jedis.Jedis")
//    public User user(){//        return new User();
//    }@Bean@ConditionOnClass("com.alibaba.fastjson.JSON")public User user(){return new User();}@Bean@ConditionalOnProperty(name = "csdn", havingValue = "xinghe")public User user2(){return new User();}
}

在启动类中添加获取 user2 的代码。

@SpringBootApplication
public class SpringbootConditionApplication {public static void main(String[] args) {// 启动SpringBoot的应用,返回Spring的IOC容器ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);// 获取Bean,redisTemplateObject redisTemplate = context.getBean("redisTemplate");System.out.println(redisTemplate);// 获取Bean,userObject user = context.getBean("user");System.out.println(user);// 获取Bean,user2Object user2 = context.getBean("user2");System.out.println(user2);}
}

因为我们没有在启动类中配置相应的属性,所以运行输出没有找到 user2。

接下来我们在 application.properties 中配置相应的属性:

csdn=xinghe

再次运行,成功输出:

总结

本章介绍了自定义的条件以及 SpringBoot 提供的常用条件注解。

自定义条件

  • 定义条件类:自定义类实现 Condition 接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean 值。
    matches 方法两个参数:

    1. context:上下文对象,可以获取属性值、类加载器、BeanFactory 等。
    2. metadata:元数据对象,用于获取注解属性。
  • 判断条件:在初始化 Bean 时,使用 @Conditional(条件类.class) 注解。

SpringBoot 提供的常用条件注解

  • ConditionalOnProperty:判断配置文件是否有对应的属性和值才初始化 Bean。
  • ConditionOnClass:判断环境中是否有对应的字节码文件才初始化 Bean。
  • ConditionOnMissingBean:判断环境中没有对应 Bean 才初始化 Bean。

SpringBoot 自动配置之 Condition相关推荐

  1. 【Spring框架家族】SpringBoot自动配置基本实现

    SpringBoot自动配置-Condition_1 Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载相应的Bean @Conditio ...

  2. SpringBoot 自动配置之 Enable 注解原理

    文章目录 前言 @Enable* 注解 思考 演示 springboot-enable-other User 类 UserConfig 配置类 springboot-enable 方案 1. 使用 @ ...

  3. SpringBoot自动配置的原理及实现

    SpringBoot的核心就是自动配置,自动配置是基于条件判断配置Bean 自动配置的源码在spring-boot-autoconfigure-2.2.13.RELEASE SpringBoot运行原 ...

  4. 这样讲 SpringBoot 自动配置原理,你应该能明白了吧

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:你在我家门口 juejin.im/post/5ce5effb ...

  5. springboot自动配置原理

    概述 Springboot的基本认识 对于Spring框架,我们接触得比较多的是Spring mvc,Spring IOC.AOP.DI.而这框架如果在使用过程中,随着项目越来越大,引入的技术越来越多 ...

  6. SpringBoot 自动配置

    Springboot 自动配置 关注 "弋凡"(YiFan)微信公众号吧 记录简单笔记 做你的最爱 Springboot 是什么呐? 我管她是什么,好用就行了啊!!! spring ...

  7. SpringBoot实战之SpringBoot自动配置原理

    www.cnblogs.com/leihuazhe/p- SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConf ...

  8. SpringBoot | 自动配置原理

    微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 这个月过去两天了,这篇文章才跟大家见面,最近比较累,大家见谅下.下班后闲着无聊看了下 SpringBoot 中的自动配置,把我 ...

  9. springboot自动配置的原理_SpringBoot自动配置原理

    SpringBoot的启动入口就是一个非常简单的run方法,这个run方法会加载一个应用所需要的所有资源和配置,最后启动应用. 通过查看run方法的源码,我们发现,run方法首先启动了一个监听器,然后 ...

最新文章

  1. 统计学 计算机论文,统计学专业论文范文
  2. Git error: Unable to negotiate with X.X.X.X : no matching host key type found . their offer: ssh-dss
  3. Django REST framework的一些奇巧淫技(干货!!!)
  4. 开发资源库(repositiory)
  5. Head First JSP---随笔四
  6. 在Unity中做一个淡入式的屏幕虚化
  7. Unity Android 动态更新 Assembly-CSharp.dll
  8. linux比windows丑,告别 Windows 难看难用,教你打造体验不输 macOS 和 Linux 的终端
  9. idea中Tomcat中文乱码问题
  10. 2.2、云计算FusionCompute计算虚拟化
  11. 使用HTML批量拼图
  12. dsf5.0 有确认按钮的弹框
  13. 最小费用最大流之 zkw费用流与普通费用流
  14. 在 Mac 山猫 10.8 中从代码编译安装 vim
  15. windows操作系统序列号大全(经典之作)
  16. UVA12627:Erratic Expansion(奇怪的气球膨胀)
  17. 微信小程序onTabItemTap和onShow的执行顺序
  18. boost------asio库的使用2(Boost程序库完全开发指南)读书笔记
  19. PCSF_46008 error
  20. 不同excel表数据汇总

热门文章

  1. Zeppelin设置返回的最大结果行数zeppelin.spark.maxResult
  2. MFC OnContextMenu
  3. Django之ContentType详解
  4. word2013-论文双栏情况下如何让图片单栏
  5. 初识HTML(二)VsCode编辑器,常用标签
  6. python科学计算——用Weave嵌入C语言
  7. 在某国使用1角、2角、5角的硬币可以组成1元钱。编程输出有多少种组成方法。
  8. 视觉slam十四讲第8章课后习题3+稀疏直接法程序注释
  9. InvocationTargetException:null 问题解决
  10. strcpy 实现原理