1.什么是@Conditional注解
@Conditional
来源于spring-context
包下的一个注解。- 通过@Conditional配置一些条件判断,当所有条件都满足时,被该@Conditional注解标注的目标才会被Spring处理。
- 例如根据当前环境、系统属性、配置文件等条件来决定是否注册某个Bean或执行某个组件。
- 应用场景
- 在某个特定的环境下,需要注册一个特定的Bean,常用的是当bean不存在的时候才注册。
- 根据配置文件中的某个属性来决定是否注册Bean。
- 根据环境选择配置类,比如当前系统的操作系统类型、版本等条件来决定是否要执行。
2.@Conditional注解源码解析
- 通过他的注解内部可以发现,他就是一个纯功能性注解,他并没有依赖于其他注解,类上只有三个元注解。
@Target({ElementType.TYPE, ElementType.METHOD}) //注解作用范围在接口、类、枚举、注解、方法 @Retention(RetentionPolicy.RUNTIME) //保留到运行期,jvm加载class文件之后,仍然存在 @Documented //生成javadoc文档 public @interface Conditional { /** * All {@link Condition} classes that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
- value:Condition类型的数组,Condition是一个接口,表示一个条件判断,内部有个方法返回true或false
- 当所有Condition条件都成立,@Conditional的结果才成立
- 疑问:那这个Condition又是个啥?
- Condition本身是个接口,源码中matches方法判断条件是否匹配,方法中有两个参数:
- context 上下文,获取容器中的bean的信息
- metadata:获取被@Conditional标注的对象上的所有注解信息
public interface Condition { //判断条件是否匹配 boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
- 我们再来看看ConditionContext的源码
public interface ConditionContext { //返回bean定义注册器,用于获取Bean定义的注册表,可以用来注册和获取bean定义的各种配置信息 BeanDefinitionRegistry getRegistry(); //用于获取Bean工厂,可以用来获取或操作Bean实例,相当于一个ioc容器对象 ConfigurableListableBeanFactory getBeanFactory(); //用于获取环境变量和属性值等配置信息 Environment getEnvironment(); //用于获取资源文件,比如XML文件、图片、文本等 ResourceLoader getResourceLoader(); //用于获取类加载器,可以用来加载类或资源文件 ClassLoader getClassLoader(); }
- 这里面主要的是BeanDefinitionRegistry ,返回bean定义注册器,用于获取Bean定义的注册表,也是我们一会要用到的。
- BeanDefinitionRegistry 源码
public interface BeanDefinitionRegistry extends AliasRegistry { //用于向Bean定义注册表中注册一个Bean定义,参数beanName表示Bean的名称,beanDefinition表示Bean的定义信息 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); //从Bean定义注册表中移除指定名称的Bean定义,参数beanName表示要移除的Bean名称 void removeBeanDefinition(String beanName) ; //获取指定名称的Bean定义,参数beanName表示要获取的Bean名称 BeanDefinition getBeanDefinition(String beanName) ; //判断指定名称的Bean定义是否存在于注册表中,参数beanName表示要判断的Bean名称 boolean containsBeanDefinition(String beanName); //获取所有Bean定义的名称,返回一个String数组 String[] getBeanDefinitionNames(); //获取Bean定义的数量。 int getBeanDefinitionCount(); //判断指定名称的Bean是否已经被使用,参数beanName表示要判断的Bean名称。如果该名称已经被使用,则返回true,否则返回false boolean isBeanNameInUse(String beanName); }
BeanDefinition简介
BeanDefinition是Spring容器中最重要的概念之一,他是容器创建和管理Bean实例的基础,对Bean的定义信息的抽象和封装
描述一个Bean的定义信息,包括Bean的名称、类型、作用域、属性等信息
可以对Bean的创建和管理进行详细的配置和控制,例如可以指定Bean的作用域、是否懒加载、是否自动注入等属性。
BeanDefinition的用途
定义Bean的基本信息,包括Bean的名称、类型、作用域等。
定义Bean的属性信息,包括Bean的属性名称、类型、值等。
定义Bean的生命周期信息,包括Bean的初始化方法、销毁方法等。
定义Bean等依赖关系,包括Bean之间的依赖关系、注入方式等。
定义Bean等AOP信息,包括切面、通知、切点等。
3.@Conditional注解案例实战
- 需求背景:现在系统有两套数据源,模拟篮球远动员,一个正式的,一个替补的,只有当正式的不能上场时,替补的才会上场。
- 编码实战
创建篮球运动员实体Bean
@Data @AllArgsConstructor @NoArgsConstructor public class BasketballPlayers { /** * 姓名 */ private String name; /** * 年龄 */ private int age; /** * 性别 */ private String sex; }
创建Bean的配置类
public class BasketballPlayersConfig { @Bean("lixiang") public BasketballPlayers BasketballPlayers1(){ return new BasketballPlayers("李祥",18,"男"); } @Bean("zhangsan") public BasketballPlayers BasketballPlayers2(){ return new BasketballPlayers("张三",18,"男"); } }
自定义Condition类
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { BeanDefinitionRegistry registry = conditionContext.getRegistry(); //不存在,才返回true boolean flag = !registry.containsBeanDefinition("lixiang"); return flag; } }
测试代码
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //扫描指定的包,包括子包 context.scan("com.lixiang"); //里面完成初始化操作,核心方法 context.refresh(); Map<String, BasketballPlayers> beansOfType = context.getBeansOfType(BasketballPlayers.class); System.out.println(beansOfType); }