1. @Conditional 어노테이션이란?
@Conditional
spring-context
패키지 아래의 주석 에서 가져옵니다 .- @Conditional을 통해 몇 가지 조건부 판단을 설정하고, 모든 조건이 충족되면 @Conditional 주석이 표시된 대상을 Spring에서 처리합니다.
-
예를 들어, 현재 환경, 시스템 속성, 구성 파일 및 기타 조건에 따라 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 조건이 true일 때 @Conditional의 결과는 true입니다.
질문: 그렇다면 이 조건은 무엇입니까?
-
조건 자체는 인터페이스입니다. 소스 코드의 matches 메소드는 조건이 일치하는지 여부를 판단합니다. 메소드에는 두 개의 매개변수가 있습니다.
-
컨텍스트 컨텍스트, 컨테이너에서 빈 정보 가져오기
-
메타데이터: @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 정의 레지스터를 반환하며, 이는 나중에 사용할 빈 정의 레지스트리를 얻는 데 사용됩니다.
-
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의 수명 주기 정보를 정의합니다.
- 빈 간의 종속성, 주입 방법 등을 포함하여 빈과 같은 종속성을 정의합니다.
- Aspect, 알림, 포인트컷 등을 포함하여 Bean 및 기타 AOP 정보를 정의합니다.
3. @조건부 주석의 경우 실전
-
요구 배경: 이제 시스템에는 농구 동원을 시뮬레이션하는 두 세트의 데이터 소스가 있습니다. 하나는 공식이고 다른 하나는 교체입니다. 공식이 경기를 할 수 없는 경우에만 교체 선수가 경기합니다.
-
실제 코딩
농구선수 엔티티 bean 생성
@Data @AllArgsConstructor @NoArgsConstructor public class BasketballPlayers { /** * 姓名 */ private String name; /** * 年龄 */ private int age; /** * 性别 */ private String sex; }
빈 구성 클래스 만들기
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); }