1. @Conditional アノテーションとは
@Conditional
spring-context
パッケージの下のコメントから来ています。- @Conditional を介していくつかの条件判定を設定します。すべての条件が満たされると、@Conditional アノテーションでマークされたターゲットが Spring によって処理されます。
-
たとえば、現在の環境、システムのプロパティ、設定ファイルなどの条件に応じて、Bean を登録するか、特定のコンポーネントを実行するかを決定します。
-
アプリケーションシナリオ
- 特定の環境では特定のBeanを登録する必要があり、Beanが存在しない場合の登録によく使用されます。
- 構成ファイル内のプロパティに従って、Bean を登録するかどうかを決定します。
- OSの種類や現在のシステムのバージョンなどの環境に応じて構成クラスを選択し、実行するかどうかを決定します。
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 条件が true の場合、@Conditional の結果は true になります。
質問: それで、この条件は何ですか?
-
条件自体はインターフェイスです。ソース コード内のmatchesメソッドは、条件が一致するかどうかを判断します。メソッドには2つのパラメータがあります。
-
context コンテキスト、コンテナ内の Bean 情報を取得します
-
メタデータ: @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 コンテナの最も重要な概念の 1 つであり、コンテナが Bean インスタンスを作成および管理し、Bean 定義情報を抽象化およびカプセル化するための基礎となります。
- Beanの名前、タイプ、スコープ、属性などのBeanの定義情報を記述します。
- Beanのスコープの指定、遅延ロードの有無、自動インジェクションの有無など、Beanの作成と管理を詳細に設定・制御できます。
- BeanDefinition の目的
- Beanの名前、タイプ、スコープなど、Beanの基本情報を定義します。
- Bean属性名、型、値などのBean属性情報を定義します。
- Beanの初期化方法や破棄方法など、Beanのライフサイクル情報を定義します。
- Bean 間の依存関係、注入メソッドなどを含む、Bean などの依存関係を定義します。
- アスペクト、通知、ポイントカットなどを含む、Bean およびその他の AOP 情報を定義します。
3. @Conditionalアノテーションの場合の実戦
-
要件の背景: 現在、システムにはバスケットボールの動員をシミュレートする 2 セットのデータ ソースがあり、1 つは公式で、もう 1 つは補欠であり、公式がプレーできない場合にのみ、補欠がプレーします。
-
コーディングの実際
バスケットボール選手エンティティ 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,"男"); } }
カスタム条件クラス
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); }